解决n皇后问题

4 投票
6 回答
13648 浏览
提问于 2025-04-16 10:43

我刚刚用Python解决了n皇后问题。这个问题的解决方案可以输出在一个nXn的棋盘上放置n个皇后的所有可能方案的总数。不过,当我尝试用n=15时,计算结果花了超过一个小时。有没有人能看看我的代码,给我一些加速这个程序的建议……我还是个初学者。

#!/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() 

6 个回答

2

我建议你看看这个链接中的 test_generators.py 文件,它是Python源代码中的一个测试文件,里面有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

可以用唐纳德·克努斯的随机估计方法来估算解决方案的数量。

一开始没有放置任何皇后,这时下一行可以放的位置有n个。随机选择一个位置,然后计算下一行可以放的位置数量(记作p),然后把这个p和n相乘,得到总的解决方案数量(total = n * p),接着再随机选择一个允许的位置。

对于这一行,计算下一行可以放的位置数量(p),然后把总的解决方案数量乘以这个p(total *= p)。一直重复这个过程,直到棋盘无法解决,这时解决方案数量就是零,或者棋盘被成功解决。

多次重复这个过程,计算平均的解决方案数量(包括零的情况)。这样可以快速且相对准确地估算解决方案的数量,运行次数越多,估算结果会越好。

希望这样解释能让你明白;)

5

解决N皇后问题的回溯算法在最坏情况下是一个阶乘算法。也就是说,当N=8时,最坏情况下需要检查8!(8的阶乘)个解,N=9时就是9!,以此类推。可以看出,可能的解的数量增长得非常快,非常大。如果你不相信,可以去计算器上从1开始连续乘数字,看看计算器多久就会没法用了。

幸运的是,并不是每一个可能的解都需要检查。不过,不幸的是,正确解的数量仍然大致遵循阶乘的增长模式。因此,这个算法的运行时间也会以阶乘的速度增长。

因为你需要找到所有正确的解,所以实际上没有太多方法可以加快程序的速度。你已经在搜索树中去掉了不可能的分支,做得很好。我认为没有其他方法会有重大影响。这就是一个比较慢的算法。

撰写回答