解决N-Queens问题......我们能走多远?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决N-Queens问题......我们能走多远?相关的知识,希望对你有一定的参考价值。
N-Queens问题:
这个问题表明,给定一个大小为N乘N的国际象棋棋盘,找到不同的排列,其中N个皇后可以放在棋盘上而没有任何一个相互威胁。
我的问题是: 程序可以在合理的时间内计算答案的N的最大值是多少?或者到目前为止我们看到的最大N是多少?
这是我在CLPFD(Prolog)中的程序:
generate([],_).
generate([H|T],N) :-
H in 1..N ,
generate(T,N).
lenlist(L,N) :-
lenlist(L,0,N).
lenlist([],N,N).
lenlist([_|T],P,N) :-
P1 is P+1,
lenlist(T,P1,N).
queens(N,L) :-
generate(L,N),lenlist(L,N),
safe(L),
!,
labeling([ffc],L).
notattack(X,Xs) :-
notattack(X,Xs,1).
notattack(X,[],N).
notattack(X,[Y|Ys],N) :-
X #= Y,
X #= Y - N,
X #= Y + N,
N1 is N + 1,
notattack(X,Ys,N1).
safe([]).
safe([F|T]) :-
notattack(F,T),
safe(T).
这个程序运行得很好,但是它花费的时间不断增加N.这是一个示例执行:
?- queens(4,L).
L = [2, 4, 1, 3] ;
L = [3, 1, 4, 2] ;
No
这意味着您将4个皇后放置在第1列的第2行,第2列的第4行,第3行的第1行和第4行的第3行(在4乘4的棋盘中)
现在让我们看看这个程序是如何执行的(计算第一个排列所花费的时间): 对于N = 4,5 ..... 10在一秒钟内计算 对于N = 11-30,需要-1-3秒 对于N = 40..50仍然在一分钟内计算 在N = 60时,它脱离了全局堆栈(搜索空间巨大)。
这是过去的家庭作业问题。 (最初的问题只是编码N-Queens)
我也有兴趣看到其他语言的替代实现(性能比我的实现更好)或者我的算法/程序还有改进的余地
Raymond hettinger在pycon:easy ai in python上提出的简短解决方案
#!/usr/bin/env python
from itertools import permutations
n = 12
cols = range(n)
for vec in permutations(cols):
if (n == len(set(vec[i] + i for i in cols))
== len(set(vec[i] - i for i in cols))):
print vec
计算所有排列是不可扩展的(O(n!)
)
这个讨论混淆了三个不同的计算问题:(1)找到N皇后问题的解决方案,(2)列出一些固定N的所有解决方案,以及(3)计算某些固定N的所有解决方案。第一个问题看起来很棘手首先是一块板的尺寸,例如N = 8。然而,正如维基百科所暗示的那样,在N很大的时候,在某些关键方面它很容易。大板上的女王不会那么多沟通。除了内存约束之外,启发式修复算法随着N的增加而变得更容易和更容易。
列出每个解决方案是另一回事。这可以通过一个良好的动态编程代码来完成,该代码的大小足够大,以至于没有必要读取输出。
最有趣的问题是计算解决方案。最新技术概括在一个名为The Encyclopedia of Integer Sequences的神话般的参考文献中。计算结果为N = 26。我猜这也使用动态编程,但与列出每个解决方案的情况不同,算法问题更深入,并且可以进一步推进。
Loren Pechtel说:“现在真的疯狂了:29秒花了9秒.30花了差不多6分钟!”
对于不同电路板尺寸的回溯复杂性,这种迷人的缺乏可预测性是我最感兴趣的这个难题的一部分。多年来,我一直在构建一个列表,列出了为每个电路板尺寸找到第一个解决方案所需的算法步骤 - 使用简单且众所周知的深度优先算法,在递归C ++函数中。
这里列出了N = 49 ......减去N = 46和N = 48的电路板的所有“计数”,这些仍在进行中:
http://queens.cspea.co.uk/csp-q-allplaced.html
(我已经在整数序列百科全书(OEIS)中列为A140450)
该页面包含指向匹配的第一个解决方案列表的链接。
(我的First Solutions列表是OEIS序列号A141843)
我并不主要记录每个解决方案需要多少处理时间,而是记录在发现每个电路板的算法优先解决方案之前需要多少个失败的女王放置。当然,后置放置的速率取决于CPU的性能,但考虑到对特定CPU和特定电路板尺寸的快速测试运行,计算解决其中一个“找到的”解决方案需要多长时间是一件容易的事。
例如,在Intel Pentium D 3.4GHz CPU上,使用单CPU线程 -
- 对于N = 35,我的节目'每秒'放置'2400万个皇后,仅花了6分钟就找到了第一个解决方案。
- 对于N = 47,我的节目'每秒'放置'2050万个皇后并且花了199天。
我目前的2.8GHz i7-860每秒大约有2860万个皇后,试图找到N = 48的第一个解决方案。到目前为止,它已经花费了超过550天(理论上,如果它从来没有不间断)到非成功地放置1,369,331,731,000,000(和快速攀爬)皇后。
我的网站(还)没有显示任何C ++代码,但我确实在该网页上给出了一个链接,以简单说明解决N = 5板所需的15个算法步骤中的每一个。
这确实是一个美味的拼图!
你使用哪种Prolog系统?例如,使用最新版本的SWI-Prolog,您可以使用原始代码在几分之一秒内轻松找到N = 80和N = 100的解决方案。许多其他Prolog系统将比这快得多。
N-queens问题甚至出现在SWI-Prolog的一个在线示例中,在SWISH中以CLP(FD) queens的形式提供。
100个皇后的例子:
?- time((n_queens(100, Qs), labeling([ff], Qs))). Qs = [1, 3, 5, 57, 59 | ...] . 2,984,158 inferences, 0.299 CPU in 0.299 seconds (100% CPU, 9964202 Lips)
SWISH还向您展示了解决方案的图像。
这是一个动画GIF,显示了使用SWI-Prolog的N = 40个皇后的完整解决方案过程:
至于什么是计算机解决的最大N,在文献中有参考文献,其中使用冲突修复算法(即局部搜索)找到了大约3 * 10 ^ 6的N的解。例如,参见[Sosic and Gu]的经典论文。
至于使用回溯的精确求解,存在一些巧妙的分支启发法,其实现了几乎没有回溯的正确配置。这些启发式方法也可用于查找问题的第一个k解决方案:在找到初始正确配置后,搜索回溯以查找附近的其他有效配置。
这些近乎完美的启发式的参考文献是[Kale 90]和[San Segundo 2011]
程序可以在合理的时间内计算答案的N的最大值是多少?或者到目前为止我们看到的最大N是多少?
没有限制。也就是说,检查解决方案的有效性比构建一个解决方案加七个对称解决方案更昂贵。
我拖出了一个旧的Delphi程序,它计算了任何给定电路板尺寸的解决方案数量,做了一个快速修改,使其在一次击中后停止,我看到数据中有一个奇怪的模式:
花费1秒钟来解决的第一块板是n = 20.21在62毫秒内解决了。 (注意:这是基于现在,而不是任何高精度系统。)22花了10秒钟,直到28秒才重复。
我不知道优化有多好,因为当优化规则非常不同时,这最初是一个高度优化的例程。我确实做了一件与大多数实现截然不同的事情 - 它没有板子。相反,我正在跟踪哪些列和对角线被攻击并且每行添加一个女王。这意味着每个单元测试3个阵列查找,根本没有乘法。 (正如我所说,从规则非常不同。)
现在真的疯了:29秒花了9秒。 30花了差不多6分钟!
实际上受限制的随机游走(生成和测试)就像bakore概述的那样,如果您只需要一些解决方案就可以了,因为这些可以快速生成。我在20或21岁的时候为一堂课做了这个,并在1987年3月的“Pascal,Ada&Modula-2期刊”,“The Queens Problem Revisited”中发表了解决方案。我今天刚刚从那篇文章中删除了代码(这是非常低效的代码)并且在修复了几个问题之后产生了N = 26 ... N = 60个解决方案。
如果你只想要1个解,那么它可以在线性时间O(N)中贪婪地找到。我在python中的代码:
import numpy as np
n = int(raw_input("Enter n: "))
rs = np.zeros(n,dtype=np.int64)
board=np.zeros((n,n),dtype=np.int64)
k=0
if n%6==2 :
for i in range(2,n+1,2) :
#print i,
rs[k]=i-1
k+=1
rs[k]=3-1
k+=1
rs[k]=1-1
k+=1
for i in range(7,n+1,2) :
rs[k]=i-1
k+=1
rs[k]=5-1
elif n%6==3 :
rs[k]=4-1
k+=1
for i in range(6,n+1,2) :
rs[k]=i-1
k+=1
rs[k]=2-1
k+=1
for i in range(5,n+1,2) :
rs[k]=i-1
k+=1
rs[k]=1-1
k+=1
rs[k]=3-1
else :
for i in range(2,n+1,2) :
rs[k]=i-1
k+=1
for i in range(1,n+1,2) :
rs[k]=i-1
k+=1
for i in range(n) :
board[rs[i]][i]=1
print "
"
for i in range(n) :
for j in range(n) :
print board[i][j],
print
然而,这里打印花费O(N ^ 2)时间并且python是一种较慢的语言,任何人都可以尝试在其他语言(如C / C ++或Java)中实现它。但即使使用python,它也会在1或2秒内获得n = 1000的第一个解决方案。
以上是关于解决N-Queens问题......我们能走多远?的主要内容,如果未能解决你的问题,请参考以下文章