解决 n 皇后难题

4

我刚刚在Python中解决了n皇后问题。该解决方案输出在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() 

1
你应该意识到你的算法是O(N!)的,对吧?没有理由相信你会从中获得更多的常数因子。 - ephemient
1
没有检查过,但维基百科上说n = 14有45k个解决方案。由于这是指数增长,可以肯定n = 15的解决方案更多。无论使用什么算法(即使使用最优算法),这都是一个复杂的问题,所以需要一些时间。建议先尝试较小的n值(比如8)。 - user395760
http://www.durangobill.com/N_Queens.html 包含了 N=26 的解决方案数量。 - 9000
如果有人在想,我已经算过了。当N=15时(2,279,184个正确解),计算一个正确解的平均时间为1.579513毫秒,总共需要60分钟。 - cledoux
6个回答

5
N皇后问题的回溯算法在最坏情况下是一个阶乘算法。因此,对于N=8,最坏情况下会检查8!个解决方案,而N=9会变为9!等等。可以看出,可能的解决方案数量非常庞大且增长迅速。如果您不相信,请打开计算器并从1开始连续相乘,然后看看计算器耗尽内存的速度。
幸运的是,并不需要检查每种可能的解决方案。不幸的是,正确解决方案的数量仍然遵循大致阶乘增长模式。因此,算法的运行时间以阶乘的速度增长。
由于您需要找到所有正确的解决方案,因此真的没有太多可以加快程序速度的方法。您已经很好地剪枝了搜索树中不可能的分支。我认为没有其他任何事情能产生重大影响。这只是一个缓慢的算法。

2

我建议您查看Python源代码中的test_generators.py,以获取N皇后问题的另一种实现方式。

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]

2
使用Donald Knuth的随机估计方法可以估计解决方案的数量。
从没有放置皇后开始,下一行允许的位置数为n。随机选择其中一个位置,计算下一行的允许位置数(p)并将其乘以n,将其作为总解决方案数(total = n * p)存储,然后随机选择其中一个允许的位置。
对于这一行,计算下一行的允许位置数(p),然后将总解决方案数乘以它(total *= p)。重复此过程,直到无法解决棋盘为止,此时解决方案的数量等于零,或者直到解决棋盘为止。
多次重复此过程,并计算平均解决方案数(包括任何零)。这应该为您提供快速且相当准确的解决方案数量近似值,近似值会随着运行次数的增加而提高。
希望这有意义;)

2

0
以下是我的PYTHON实现。您可能想使用PYPY来获得更快的速度。
通过使用O(1)时间方法来检查下一个皇后是否受到已放置在棋盘上的皇后的攻击,可以提高其速度。
假设程序为“nqueen.py”,运行它的示例是“python nqueen.py 6”,其中6是6x6棋盘的大小。
#!/bin/python
#
# TH @stackoverflow, 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)

0
我们也可以使用遗传算法来解决n皇后问题。这在edX课程ColumbiaX: CSMM.101x人工智能(AI)的其中一节讲座中https://youtu.be/l6qll5OldHQ有所描述。
目标函数试图优化皇后之间不相互攻击的对数。下面的动画展示了一个R语言中n=20的例子解决方案。关于如何使用遗传算法解决n皇后问题的更多细节可以在这里找到:https://sandipanweb.wordpress.com/2017/03/09/solving-the-n-queen-puzzle-with-genetic-algorithm-in-r/?frame-nonce=76cf9b156a

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接