并發(fā)實現(xiàn)n皇后問題

前言

最近在用Go并發(fā)實現(xiàn)n皇后問題中遇到很多的坑础倍,在此進行記錄擎宝。

n后問題以及單線程實現(xiàn)

n后問題:簡單來說就是在n*n的棋盤上放置n個皇后翘地,使每兩者之間都不在同一行,同一列拦惋,同一斜線上。求滿足條件的解的個數(shù)安寺?

// 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))
}

錯誤的并發(fā)代碼

思路:創(chuàng)建協(xié)程去執(zhí)行 NQueen 函數(shù),并將結(jié)果放入 channel挑庶,最后累加得到最終結(jié)果言秸。于是,代碼可能是下面這個樣子的:

// 其他函數(shù)不變
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
}

這樣迎捺,你可能發(fā)現(xiàn)举畸,答案并不是我們預期的那樣。
分析:
(1)這里只傳入同一個 process 實例凳枝,那么多個協(xié)程同時對他們操作抄沮,不加以控制的話,肯定會出現(xiàn)問題岖瑰;所以我們給每個協(xié)程一份單獨的 process 實例叛买。
(2)第二個,我們著重來講 process.array = append(process.array, j) 問題蹋订。

append函數(shù)機制

首先明確我們的需求:重新開辟內(nèi)存空間率挣,值為原來數(shù)組切片基礎(chǔ)上,增加一個元素
append機制:
當切片還有可用容量露戒,調(diào)用append椒功,會將值直接加到切片中捶箱,這時原切片和現(xiàn)切片共用底層數(shù)組,所以地址相同蛾茉。
而當容量不足讼呢,系統(tǒng)會重新開辟一塊兒內(nèi)存,所以原切片和現(xiàn)切片底層數(shù)組不同谦炬。

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)
}

所以悦屏,我們每次初始化新的切片,使系統(tǒng)分配不同的內(nèi)存空間键思。

代碼實現(xiàn)

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))
}

如何限流

何為限流础爬,就是同一時刻創(chuàng)建恒定個協(xié)程。這里我們用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))
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末看蚜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赔桌,更是在濱河造成了極大的恐慌供炎,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疾党,死亡現(xiàn)場離奇詭異音诫,居然都是意外死亡,警方通過查閱死者的電腦和手機雪位,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門竭钝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人雹洗,你說我怎么就攤上這事香罐。” “怎么了时肿?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵庇茫,是天一觀的道長。 經(jīng)常有香客問我螃成,道長港令,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任锈颗,我火速辦了婚禮顷霹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘击吱。我一直安慰自己淋淀,他們只是感情好,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布覆醇。 她就那樣靜靜地躺著朵纷,像睡著了一般炭臭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袍辞,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天鞋仍,我揣著相機與錄音,去河邊找鬼搅吁。 笑死威创,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的谎懦。 我是一名探鬼主播肚豺,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼界拦!你這毒婦竟也來了吸申?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤享甸,失蹤者是張志新(化名)和其女友劉穎截碴,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛉威,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡隐岛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓷翻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡割坠,死狀恐怖齐帚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情彼哼,我是刑警寧澤对妄,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站敢朱,受9級特大地震影響剪菱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拴签,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一孝常、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚓哩,春花似錦构灸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稠氮。三九已至,卻和暖如春半开,著一層夾襖步出監(jiān)牢的瞬間隔披,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工寂拆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奢米,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓漓库,卻偏偏與公主長得像恃慧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子渺蒿,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容