解决 n-queen 谜题
Posted
技术标签:
【中文标题】解决 n-queen 谜题【英文标题】:Solving the n-queen puzzle 【发布时间】:2011-01-27 15:20:52 【问题描述】:我刚刚解决了python中的nqueen问题。该解决方案输出在 nXn 棋盘上放置 n 个皇后的解决方案总数,但在 n=15 的情况下尝试它需要一个多小时才能得到答案。任何人都可以看一下代码并给我一些加速这个程序的提示......一个新手python程序员。
#!/usr/bin/env python2.7
##############################################################################
# a script to solve the n queen problem in which n queens are to be placed on
# an nxn chess board in a way that none of the n queens is in check by any other
#queen using backtracking'''
##############################################################################
import sys
import time
import array
solution_count = 0
def queen(current_row, num_row, solution_list):
if current_row == num_row:
global solution_count
solution_count = solution_count + 1
else:
current_row += 1
next_moves = gen_nextpos(current_row, solution_list, num_row + 1)
if next_moves:
for move in next_moves:
'''make a move on first legal move of next moves'''
solution_list[current_row] = move
queen(current_row, num_row, solution_list)
'''undo move made'''
solution_list[current_row] = 0
else:
return None
def gen_nextpos(a_row, solution_list, arr_size):
'''function that takes a chess row number, a list of partially completed
placements and the number of rows of the chessboard. It returns a list of
columns in the row which are not under attack from any previously placed
queen.
'''
cand_moves = []
'''check for each column of a_row which is not in check from a previously
placed queen'''
for column in range(1, arr_size):
under_attack = False
for row in range(1, a_row):
'''
solution_list holds the column index for each row of which a
queen has been placed and using the fact that the slope of
diagonals to which a previously placed queen can get to is 1 and
that the vertical positions to which a queen can get to have same
column index, a position is checked for any threating queen
'''
if (abs(a_row - row) == abs(column - solution_list[row])
or solution_list[row] == column):
under_attack = True
break
if not under_attack:
cand_moves.append(column)
return cand_moves
def main():
'''
main is the application which sets up the program for running. It takes an
integer input,N, from the user representing the size of the chessboard and
passes as input,0, N representing the chess board size and a solution list to
hold solutions as they are created.It outputs the number of ways N queens
can be placed on a board of size NxN.
'''
#board_size = [int(x) for x in sys.stdin.readline().split()]
board_size = [15]
board_size = board_size[0]
solution_list = array.array('i', [0]* (board_size + 1))
#solution_list = [0]* (board_size + 1)
queen(0, board_size, solution_list)
print(solution_count)
if __name__ == '__main__':
start_time = time.time()
main()
print(time.time()
【问题讨论】:
您确实意识到您的算法是 O(N!),对吧?没有理由相信你会得到比从中挤出的恒定因素更多的东西。 没有检查,但是 wiki 说 n = 14 有 45k 的解决方案。由于这是一个指数增长,你可以打赌它对于 n = 15 会更多。会 i> 需要一段时间,无论算法如何(即使使用最优算法,这也是一个复杂的问题 - 而你的问题可能不是最优的)。尝试更小的 n(例如 8)。 durangobill.com/N_Queens.html 包含最多 N=26 个解。 如果有人想知道,我算了一下。由于耗时 60m,计算 N=15 的单个正确解(2,279,184 个正确解)的平均时间为 1.579513ms。 【参考方案1】:N-Queens 问题的回溯算法是最坏情况下的阶乘算法。所以对于 N=8, 8!在最坏的情况下检查解决方案的数量,N = 9 使其成为 9!,等等。可以看出,可能的解决方案的数量增长非常大,非常快。如果你不相信我,就去计算器,从 1 开始乘以连续的数字。让我知道计算器内存耗尽的速度。
幸运的是,并非所有可能的解决方案都必须检查。不幸的是,正确解决方案的数量仍然大致遵循阶乘增长模式。因此,算法的运行时间以阶乘的速度增长。
由于您需要找到所有正确的解决方案,因此在加快程序速度方面确实无能为力。您已经在从搜索树中修剪不可能的分支方面做得很好。我不认为还有什么会产生重大影响的。这只是一个缓慢的算法。
【讨论】:
【参考方案2】:可以使用 Donald Knuth 的随机估计方法来估计解决方案的数量。
从没有放置皇后开始,下一行的允许位置数为 n。 随机选择一个位置并计算下一行的允许位置数 (p) 并将其乘以 n 并将其存储为解决方案的总数 (total = n * p) ,然后随机选择一个允许位置.
对于这一行,计算下一行的允许位置数 (p),并将解的总数乘以该行数 (total *= p)。重复此操作,直到棋盘无法解出(在这种情况下解数为零),或者直到棋盘解出为止。
重复多次并计算解决方案的平均数量(包括任何零)。这应该可以为您提供一个快速且非常准确的解决方案数量近似值,并且该近似值会提高您执行的更多运行次数。
我希望这是有道理的;)
【讨论】:
【参考方案3】:我建议您查看 Python 源代码中的 test_generators.py
,以了解 N-Queens 问题的替代实现。
Python 2.6.5 (release26-maint, Sep 12 2010, 21:32:47)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from test import test_generators as tg
>>> n= tg.Queens(15)
>>> s= n.solve()
>>> next(s)
[0, 2, 4, 1, 9, 11, 13, 3, 12, 8, 5, 14, 6, 10, 7]
>>> next(s)
[0, 2, 4, 6, 8, 13, 11, 1, 14, 7, 5, 3, 9, 12, 10]
【讨论】:
【参考方案4】:刚看到这个问题。我想贡献 2 个解决方案。
This one returns all distinct solutions
This one returns the first valid solution found
非常感谢您的反馈。干杯
【讨论】:
【参考方案5】:下面是我的 PYTHON 实现。您可能希望使用 PYPY 来提高速度。
它的速度是通过使用 O(1) 时间方法来检查下一个皇后是否被已经放置在棋盘上的人攻击来帮助提高的。
假设程序是“nqueen.py”,运行它的示例是“python nqueen.py 6”,其中 6 是 6x6 板的大小。
#!/bin/python
#
# TH @***, 2016-01-20, "N Queens" with an O(1) time method to check whether the next queen is attacked
#
import sys
board_size = int(sys.argv[1])
Attacked_H = i:0 for i in range(0, board_size)
Attacked_DU = i:0 for i in range(0, board_size*2)
Attacked_DD = i:0 for i in range(-board_size, board_size)
def nqueen(B, N, row, col):
if(row >= N):
return 1
if(col >= N):
print "board:\n" + str.join("\n", ["_|"*q + "Q|" + "_|"*(board_size - q - 1) for q in B])
return 0
B[col] = row
if(0==(Attacked_H[row] + Attacked_DU[row+col] + Attacked_DD[row-col])):
[Attacked_H[row], Attacked_DU[row+col], Attacked_DD[row-col]] = [ 1,1,1 ]
nqueen(B, N, 0, col + 1)
[Attacked_H[row], Attacked_DU[row+col], Attacked_DD[row-col]] = [ 0,0,0 ]
nqueen(B, N, row + 1, col)
nqueen(list(range(0, board_size)), board_size, 0, 0)
【讨论】:
【参考方案6】:我们也可以用遗传算法解决 n-queens。在 edX 课程 ColumbiaX:CSMM.101x 人工智能 (AI) 的讲座之一中,https://youtu.be/l6qll5OldHQ 对此进行了描述。
目标函数试图优化非攻击皇后对的数量。 下面的动画显示了 R 中 n=20 的示例解决方案。有关如何使用遗传算法解决 n-queens 的更多详细信息,请参见:https://sandipanweb.wordpress.com/2017/03/09/solving-the-n-queen-puzzle-with-genetic-algorithm-in-r/?frame-nonce=76cf9b156a。
【讨论】:
以上是关于解决 n-queen 谜题的主要内容,如果未能解决你的问题,请参考以下文章