为什么偶数N比奇数N花费更长的时间?

时间:2019-04-22 13:41:26

标签: python backtracking n-queens

我这里有一些代码可以在python中使用回溯来解决n个皇后区问题。当我运行它时,赔率总是比赔率花费的时间少得多。当n达到20+左右时,这一点尤其明显。有人知道为什么吗?

import time
global N
N = int(input("Enter N: "))


def display_solution(board):
    print('\n'.join(['\t'.join([str(cell) for cell in row]) for row in 
    board]))


def safe(board, row, col):
    for i in range(col):
        if board[row][i] == 1:
            return False
    for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
        if board[i][j] == 1:
            return False
    for i, j in zip(range(row, N, 1), range(col, -1, -1)):
        if board[i][j] == 1:
            return False
    return True


def solve_help(board, col):
    if col >= N:
        return True

    for i in range(N):
        if safe(board, i, col):
            board[i][col] = 1
            if solve_help(board, col + 1) is True:
                return True
            board[i][col] = 0
    return False


def solve():
    board = [[0 for x in range(N)] for y in range(N)]
    if solve_help(board, 0) is False:
        print("Solution does not exist")
        return False
    display_solution(board)
    return True


start = time.clock()
solve()
end = time.clock()
print(N, "\t", end-start)

我假设它必须与对角线的偶数而不是偶数有关。我也不确定这是该问题的所有回溯算法还是仅此特定代码存在的问题。无论哪种方式,谢谢您的帮助。

1 个答案:

答案 0 :(得分:0)

当在第一列中的一个发生回溯并且必须尝试下一行时,该算法花费大量时间。而且,将奇数N板与N-1板进行比较确实表明,偶数板的解决方案通常需要执行更多此类回溯/重试下一个处理。例如,对于N = 19,解决方案的左上角看起来像这样:

1 0 0 0 0
0 0 0 1 0
0 1 0 0 0
0 0 0 0 1*
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

很快找到了前五列中的这五个皇后,因为它们是第一个不与先前皇后相撞的。而且显然可以放置其他女王/王后,而不必重新考虑前五个女王。

对于N = 18,解决方案的同一角如下所示:

1 0 0 0 0
0 0 0 1 0
0 1 0 0 0
0 0 0 0 0-
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 1*

请注意标有减号的位置。这看起来很有希望(就像19局那样):在得出无法正确放置其他皇后的结论之前,其调查花费了相当长的时间。这种早期的失败代价。

因此找到19板的解决方案要比18板的解决方案早。

请注意,解决27问题所需的时间比解决26问题所需的时间略长,但这并不重要:看起来时间复杂度为 O(2 n ,因此要比较不同板尺寸的时间,最好在对数Y轴上进行:

enter image description here

“工作”表示执行功能safe的次数。

这种算法是否总是花费相对于 更长的时间(与N + 1电路板所需的时间相比),但是对于这几个电路板尺寸来说似乎与该算法从左上角开始自然形成的骑士跳跃有关。请注意,此模式对于5号和7号电路板是如何完美发挥作用的:解决方案的一部分始终是下一个皇后可以坐在而不干扰先前放置的皇后的位置。对于4号和6号电路板,甚至没有任何解决方案,角点处有一个女王/王后,这就是该算法的起点。

替代品

要从程序员的角度考虑这个问题,有一种补救方法可以使差异(平均)消失:以不同(甚至随机)的顺序选择列。事实证明,采用正常顺序是获得解决方案效率较低的方法之一。

轮班选择

此算法的一个简单转变,除非所有其他失败,否则您不会考虑前两行,已经大大改变了统计信息:

solve_help中进行以下更改:

for i in range(N):

收件人:

for i in range(N):
   i = (i + 2) % N

enter image description here

看看现在平均性能如何改善: log(work)/ n 的所有度量均低于1,n = 6除外。而且:偷看现在更多是因为N的奇数。

随机选择

for i in random.sample(range(N), N):

以下是这种随机运行的结果:

enter image description here

统计数据比原始订单好得多!当然,您有时会得到错误的统计信息,因为它是随机的。但平均而言,它的性能要好得多。

其他非随机顺序的方法可以包括col和具有不同系数的N//2,例如i = (i + col*2 + N//2) % N等。但是,请参阅下面的最后说明。

其他备注

我将应用以下优化:跟踪已采用的行,正向“对角线”和向后“对角线”。您可以为此使用列表或集合。请注意,如果两个像元的坐标之和相等,则它们在同一对角线中。向后对角线上的像元的共同点是它们的坐标之差相等。这样,您不必每次都在这些行中扫描“ 1”。

此外,board可能只是列号的列表。无需存储所有这些零。保留该内容仅用于显示。

最后,有一些简单的方法可以在线性时间内获得解决方案。参见Wikipedia