n后问题_局部搜索算法
题目
局部搜索解n皇后,并测试n的极限
回溯法难以求解更大规模的n皇后问题,但是基于概率的局部搜索算法可以解决一定规模上的 n 皇后问题
思路
根据课件上的局部搜索算法思路,n皇后具体流程为:
- 随机将 N 皇后分布在棋盘上,并保证每行每列仅有一个皇后
- 计算皇后间的冲突数 conflicts,注意这里只需要计算斜线的冲突,行列不需要
- 如果冲突为0则转(6)
- 对于棋盘上的任意两个皇后,交换位置,如果交换后冲突减少,则接受交换,更新冲突数 conflicts
- 如果陷入了局部极小,即交换了所有皇后后,冲突数不能下降,则转(1)
- 输出结果,结束
优化思路
对于上面的思路,现提供 QS2 算法思路,主要优化点在于交换策略不再是任意交换,而是选择陷入冲突的棋子进行交换
- 随机地将N 个皇后分布在棋盘上,每行、每列只有一个皇后。
- 计算皇后间的冲突数Conflicts
- 如果冲突数等于0等转(9)
- 获取棋盘上陷入冲突的所有棋子
- 将冲突棋子和其他棋子进行交换,冲突降低则更新棋盘
- 重新计算冲突,冲突为0则转(9)
- 加速: 若冲突数小于某个阈值,则更新阈值并重新获取冲突 棋子
- 如果陷入了局部极小(达到循环次数上限),即交换了所有的皇后后,冲突数仍不能下降,则转(1)
- 输出结果并结束
源代码
package main
import (
"fmt"
"math/rand"
"time"
)
const (
// 加速系数
speedUpFactor = 0.25
// 循环系数
cycleFactor = 32
)
// n 后问题:局部搜索法
func NQueenLocalSearch(n int) {
c := n
downSlash, upSlash := make([]int, 2*n), make([]int, 2*n)
var s []int
// 从这里开始计时
start := time.Now()
outer:
for c > 0 {
// 1. 随机生成棋盘
s = RandomCreateNQByArray(n)
// 2. 获取棋盘中的冲突
c := GetConflictsByArray(s, downSlash, upSlash)
// 3. 如果冲突为0,转 7
if c == 0 {
break outer
}
// 4. 获取棋盘上陷入冲突的所有棋子
attacks := GetAttack(s, downSlash, upSlash)
limit := int(speedUpFactor * float64(c))
loopSteps := 0
for loopSteps < cycleFactor*n {
for k := 0; k < len(attacks); k++ {
i := attacks[k]
j := randInt(i, n)
// 5. 对冲突棋子进行模拟交换的冲突计算,若冲突减少,则更新棋盘,否则继续取其他冲突棋子
if c1 := calConflictBeforeSwap(s, downSlash, upSlash, i, j); c1 < 0 {
c += c1
// 更新交换后的棋盘
updateQueen