美文网首页程序员
并发实现n皇后问题

并发实现n皇后问题

作者: lizhuoming | 来源:发表于2018-09-15 22:54 被阅读0次

    前言

    最近在用Go并发实现n皇后问题中遇到很多的坑,在此进行记录。

    n后问题以及单线程实现

    n后问题:简单来说就是在n*n的棋盘上放置n个皇后,使每两者之间都不在同一行,同一列,同一斜线上。求满足条件的解的个数?

    // array[i] 代表第 i 行n皇后放置位置为第 arry[i] 列,index代表当前要放置皇后的行号
    type Process struct {
        array []int
        index int
    }
    
    // 检查该行放置后会不会冲突
    func check(process Process) bool {
        array := process.array
        k := process.index
        for i := 0; i < k; i++ {
            if array[i] == array[k] {
                return true
            }
    
            if int(math.Abs(float64(array[k]-array[i]))) == k-i {
                return true
            }
        }
    
        return false
    }
    
    func NQueen(process Process, n int) int {
        if process.index >= n {
            return 1
        }
    
        res := 0
    
        for j := 0; j < n; j++ {
            process.array = append(process.array, j)
            
            if check(process) == false {
                process.index++
                res += NQueen(process, n)
                process.index--
            }
    
            process.array = process.array[:len(process.array)-1]
        }
        return res
    }
    
    func Provide(n int) int {
        process := Process{
            array: make([]int, 0),
            index: 0,
        }
        return NQueen(process, n)
    }
    
    func main() {
        fmt.Println(Provide(12))
    }
    
    

    错误的并发代码

    思路:创建协程去执行 NQueen 函数,并将结果放入 channel,最后累加得到最终结果。于是,代码可能是下面这个样子的:

    // 其他函数不变
    func NQueen(ch chan int, process Process, n int) {
        if process.index >= n {
            ch <- 1
            return
        }
    
        count := 0
        newch := make(chan int, n)
        for j := 0; j < n; j++ {
            process.array = append(process.array, j)
    
            if check(process) == false {
                process.index++
                count++
                go NQueen(newch, process, n)
                process.index--
            }
    
            process.array = process.array[:len(process.array)-1]
        }
    
        res := 0
        for i := 0; i < count; i++ {
            res += <-newch
        }
        ch <- res
    }
    
    func Provide(n int) int {
        process := Process{
            array: make([]int, 0),
            index: 0,
        }
        ch := make(chan int, 1)
        NQueen(ch, process, n)
        return <-ch
    }
    

    这样,你可能发现,答案并不是我们预期的那样。
    分析:
    (1)这里只传入同一个 process 实例,那么多个协程同时对他们操作,不加以控制的话,肯定会出现问题;所以我们给每个协程一份单独的 process 实例。
    (2)第二个,我们着重来讲 process.array = append(process.array, j) 问题。

    append函数机制

    首先明确我们的需求:重新开辟内存空间,值为原来数组切片基础上,增加一个元素
    append机制:
    当切片还有可用容量,调用append,会将值直接加到切片中,这时原切片和现切片共用底层数组,所以地址相同。
    而当容量不足,系统会重新开辟一块儿内存,所以原切片和现切片底层数组不同。

    func main() {
        ch := make([]int, 5)
        ch = append(ch, 1, 2, 3, 4)
        fmt.Printf("%p\n", ch)
        aa := append(ch, 5)
        fmt.Printf("%p\n", aa)
        bb := append(ch, 5, 6)
        fmt.Printf("%p\n", bb)
    }
    

    所以,我们每次初始化新的切片,使系统分配不同的内存空间。

    代码实现

    type Process struct {
        array []int
        index int
    }
    
    func check(process Process) bool {
        array := process.array
        k := process.index - 1
        for i := 0; i < k; i++ {
            if array[i] == array[k] {
                return true
            }
    
            if int(math.Abs(float64(array[k]-array[i]))) == k-i {
                return true
            }
        }
    
        return false
    }
    
    func NQueen(ch chan int, process Process, n int) {
        if process.index >= n {
            ch <- 1
            return
        }
    
        count := 0
        newch := make(chan int, n)
        for j := 0; j < n; j++ {
            newProcess := Process{
                array: make([]int, 0),
                index: process.index + 1,
            }
            newProcess.array = append(newProcess.array, process.array...)
            newProcess.array = append(newProcess.array, j)
            if check(newProcess) == false {
                count++
                go NQueen(newch, newProcess, n)
            }
    
        }
    
        res := 0
        for i := 0; i < count; i++ {
            res += <-newch
        }
        ch <- res
    }
    
    func Provide(n int) int {
        process := Process{
            array: make([]int, 0),
            index: 0,
        }
        ch := make(chan int, 1)
        NQueen(ch, process, n)
        return <-ch
    }
    
    func main() {
        fmt.Println(Provide(8))
    }
    

    如何限流

    何为限流,就是同一时刻创建恒定个协程。这里我们用channel来进行限流。

    type Process struct {
        array []int
        index int
    }
    
    func check(process Process) bool {
        array := process.array
        k := process.index - 1
        for i := 0; i < k; i++ {
            if array[i] == array[k] {
                return true
            }
    
            if int(math.Abs(float64(array[k]-array[i]))) == k-i {
                return true
            }
        }
    
        return false
    }
    
    var channel = make(chan int, 5)
    
    func NQueen(ch chan int, process Process, n int) {
        <-channel
        if process.index >= n {
            ch <- 1
            return
        }
    
        count := 0
        newch := make(chan int, n)
        for j := 0; j < n; j++ {
            newProcess := Process{
                array: make([]int, 0),
                index: process.index + 1,
            }
            newProcess.array = append(newProcess.array, process.array...)
            newProcess.array = append(newProcess.array, j)
            if check(newProcess) == false {
                channel <- 1
                count++
                go NQueen(newch, newProcess, n)
            }
    
        }
    
        res := 0
        for i := 0; i < count; i++ {
            res += <-newch
        }
        ch <- res
    }
    
    func Provide(n int) int {
        process := Process{
            array: make([]int, 0),
            index: 0,
        }
        ch := make(chan int, 1)
        channel <- 1
        NQueen(ch, process, n)
        return <-ch
    }
    
    func main() {
        fmt.Println(Provide(8))
    }
    

    相关文章

      网友评论

        本文标题:并发实现n皇后问题

        本文链接:https://www.haomeiwen.com/subject/crnzgftx.html