八皇后谜题是在8×8棋盘上放置八个国际象棋皇后的问题,这样就没有两个皇后相互威胁。因此,解决方案要求没有两个皇后共享相同的行,列或对角线。八个皇后拼图是在n×n棋盘上放置n个皇后的更一般的n-queens问题的一个例子,其中除了n = 2或n = 3之外,所有自然数n都存在解。
但是,有没有人可以帮助我在递归方法中无限循环地解决这个问题?
ps:你可以复制/粘贴到游乐场并尝试一下,谢谢!
class ChessBoard {
var limit: Int
var queens = [Queen]()
init(limit: Int) {
self.limit = limit
}
// Check if (i,j) is a safe position for queen
func isSafeForQueen(atRow row: Int, col: Int) -> Bool {
for q in queens {
// not in same row
if q.row == row { return false }
// not in same column
if q.col == col { return false }
// not in same diagol line
if abs(q.row-row) == abs(q.col-col) { return false }
}
return true
}
// recursive method
func dropQueen(atRow r: Int, c: Int) {
// running into last row
if r == limit {
if queens.count < 8 {
queens.removeLast()
let q = queens.last!
dropQueen(atRow: q.row, c: q.col+1)
}
output() // if success, log out the positions
return
}
// running into last column of current row
if c == limit {
queens.removeLast()
let q = queens.last!
// if no position for queen at current row, then back to last row
dropQueen(atRow: r-1, c: q.col+1)
}
// if this postion is safe for queen, then drop the queen and try next row; if not, try next spot
if isSafeForQueen(atRow: r, col: c) {
let q = Queen(row: r, col: c)
queens.append(q)
dropQueen(atRow: r+1, c: c)
} else {
dropQueen(atRow: r, c: c+1)
}
}
func play() {
dropQueen(atRow: 0, c: 0) // game will start at(0,0)
}
func output() -> String {
var s = ""
for q in queens {
s += "(\(q.row),\(q.col))"
}
return s
}
}
struct Queen {
var row: Int
var col: Int
}
// Tesing:
let b = ChessBoard(limit: 8)
//b.play()
答案 0 :(得分:1)
看起来你在理解递归的概念时遇到了麻烦。递归并不意味着所有都成为递归调用。在函数dropQueen
中,您正在使用递归调用,就像它们是gotos一样。
特别是这一个:
dropQueen(atRow: r-1, c: q.col+1)
这显然是一种做回溯的尝试。下定决心;要么回溯还是使用递归!在递归中,回到之前的决策是return
。如有必要,返回一个布尔值,让调用者知道递归调用是否找到了解决方案(return true
)或不是(return false
)。如果您希望程序找到所有可能的解决方案,而不仅仅是第一个解决方案,则不需要这样做。
引起我注意的另一个电话:
dropQueen(atRow: r, c: c+1)
这里的递归调用过度,令人困惑;使用循环扫描所有可能的列。顺便说一句,通过进行此更改,您将发现整个第二个函数参数变得多余。
这让我们只留下了一个拒绝的电话;这绰绰有余。
dropQueen(atRow: r+1, c: c)
正如vacawama所指出的,第二个参数c: c
似乎是错误的。当你前进一行(r+1
)时,你为什么要排除你放在棋盘上的最后一位女王的左的所有内容?
如果你想做递归,那么考虑你的递归函数应该做什么。通常,您希望它在已经由 r 皇后占用的板上放置(8- r )更多的皇后。您正在逐行工作,因此在您的方法 r 中只是当前行号。考虑如何根据行 r +1的解决方案来表达行 r 的问题解决方案。对最后一行做一个特殊的例外;一旦 r 等于限制,解决方案就是微不足道的(需要放置零皇后);你完成了。
哦,请重命名函数dropQueen
;这个名字清楚表明你正在用错误的心态来处理这个问题。到现在为止,你应该能够找到更合适的东西。
修改强>
您已经付出了相当大的努力来完成这项工作,因此我将与您分享我的解决方案。它使用您的原始代码(如您的问题所示),但函数dropQueen
完全重写(并且一个参数更少)。注意它是多么简单和简单;这是典型的递归。
func dropQueen(atRow r: Int) {
if queens.count < limit {
for col in 0...limit-1 {
if isSafeForQueen(atRow: r, col: col) {
let q = Queen(row: r, col: col)
queens.append(q)
dropQueen(atRow: r+1)
if queens.count == limit {
return
}
queens.removeLast()
}
}
}
}
作为奖励,如果您将return
替换为println(output())
,则该程序将打印所有92个解决方案。您可以在以下位置查看此操作:
http://swiftstub.com/923601919/
答案 1 :(得分:0)
我改变了代码:
首先,我为特定目的制作了dropQueen()和takeQueen():
func dropQueen(atRow r: Int, col: Int) {
let q = Queen(row: r, col: col)
queens.append(q)
}
func takeLastQueen() -> (Int, Int) {
let q = queens.last
if q != nil {
queens.removeLast()
}
// return (-1,0) means it's time to stop the game
return q != nil ? (q!.row, q!.col) : (-1,0)
}
然后,使用for循环来迭代同一行的每一列,并且只使用递归进行回溯(当然,我重命名了一个名为makeStep()的函数,并且当row == limit时首先添加“Done”检查)
func makeStep(atRow r: Int, fromCol: Int) {
if r == limit {
// When find a resolution, keep the result
// But it won't stop
output()
} else if r < 0 {
// Time to stop game
// but why just 27 resolutions ?
return
}
var isQueenAtCurrentRow = false
for c in fromCol..<limit {
if isSafeForQueen(atRow: r, col: c) {
dropQueen(atRow: r, col: c)
isQueenAtCurrentRow = true
break
}
}
// Only use recursion for backtrack
if isQueenAtCurrentRow {
// if queen is set, go to next row
makeStep(atRow: r+1, fromCol: 0)
} else {
// if failed, go back to last row and try next spot
let (r,c) = backTrackForNext()
makeStep(atRow: r, fromCol: c)
}
}
backTrackForNext()函数用于返回最后一行并找到最后一个女王的下一个位置。如果下一个位置没有棋盘,那么再回去
func backTrackForNext() -> (Int, Int) {
// find last row
var (r,c) = takeLastQueen()
c++ // find next spot
if c == limit {
// if hit end of column, go back again
backTrackForNext()
}
return (r,c)
}
一旦找到结果,它就不会停止,直到我在操场上得到27个结果。我还没弄清楚为什么是27岁,但是你们这些人帮助我很棒。