2015-07-19 100 views
3

我是一个Prolog的总新手(如:我只用7种语言在7周内完成了Prolog章节),所以对以下任何代码的一般评论都非常受欢迎。Prolog - jigoku求解器 - 运行时间

首先:什么是jigoku?这就像一个数独,除了你得到一个空网格,并且在每个3×3的块内,给出了相邻时隙之间的不等式。示例:http://krazydad.com/jigoku/books/KD_Jigoku_CH_8_v18.pdf。您仍然需要填写网格,以便每行,每列和每个块包含数字1-9。

我试着实现一个基于这个数独求解器的求解器:http://programmablelife.blogspot.co.uk/2012/07/prolog-sudoku-solver-explained.html。为了调试原因,我开始与该作品真的很好一个4x4例如:

:- use_module(library(clpfd)). 

small_jidoku(Rows, RowIneqs, ColIneqs) :- 
    Rows = [A,B,C,D], 
    append(Rows, Vs), Vs ins 1..4, 
    maplist(all_distinct, Rows), 
    transpose(Rows, Columns),  
    maplist(all_distinct, Columns), 
    blocks(A, B), blocks(C,D), 
    maplist(label, Rows), 
    fake_check_ineqs(Rows, RowIneqs), 
    fake_check_ineqs(Columns, ColIneqs), 
    pretty_print([A,B,C,D]).  

blocks([], []).  
blocks([A,B|Bs1], [D,E|Bs2]) :-  
    all_distinct([A,B,D,E]),  
    blocks(Bs1, Bs2). 

fake_check_ineqs([],[]). 
fake_check_ineqs([Head|Tail], [Ineq1|TailIneqs]) :- 
    Head = [A,B,C,D], 
    atom_chars(Ineq1, [X1,X2]), 
    call(X1, A, B), 
    call(X2, C, D), 
    fake_check_ineqs(Tail, TailIneqs). 

pretty_print([]). 
pretty_print([Head | Tail]) :- 
print(Head), 
print('\n'), 
pretty_print(Tail). 

我再解决下面的例子:

time(small_jidoku([[A1,A2,A3,A4],[B1,B2,B3,B4],[C1,C2,C3,C4],[D1,D2,D3,D4]],[><,<>,<<,<<],[><,<<,<>,>>])). 

这运行在大约0.5秒上衣。不过,我也试图用它来解决它

time(small_jidoku([A,B,C,D],[><,<>,<<,<<],[><,<<,<>,>>])). 

这似乎需要时间。 任何人都可以解释为什么它会花费更长的时间,当我没有指定每行有4个元素?我的天真答案是,Prolog,如果没有告诉我的行的实际格式,也会尝试探索更小/更大的行,从而浪费了时间。长度为5的行,但这是真的吗?

我的第二个问题是关于9x9版本,这非常像4x4,除了块当然更大,并且在检查不等式时还有更多的测试需要完成。代码如下:

:- use_module(library(clpfd)). 

jidoku(Rows, RowIneqs, ColIneqs) :- 
    Rows = [A,B,C,D,E,F,G,H,I], 
    append(Rows, Vs), Vs ins 1..9, 
    maplist(all_distinct, Rows), 
    transpose(Rows, Columns),  
    maplist(all_distinct, Columns),   
    blocks(A, B, C), blocks(D, E, F), blocks(G, H, I),  
    maplist(label, Rows), 
    check_ineqs(Rows, RowIneqs), 
    check_ineqs(Columns, ColIneqs), 
    pretty_print([A,B,C,D,E,F,G,H,I]).  

blocks([], [], []).  
blocks([A,B,C|Bs1], [D,E,F|Bs2], [G,H,I|Bs3]) :-  
    all_distinct([A,B,C,D,E,F,G,H,I]),  
    blocks(Bs1, Bs2, Bs3). 

check_ineqs([],[]). 
check_ineqs([Head|Tail], [Ineq1|TailIneqs]) :- 
    Head = [A,B,C,D,E,F,G,H,I], 
    atom_chars(Ineq1, [X1, X2, X3, X4, X5, X6]), 
    call(X1, A, B), 
    call(X2, B, C), 
    call(X3, D, E), 
    call(X4, E, F), 
    call(X5, G, H), 
    call(X6, H, I), 
    check_ineqs(Tail, TailIneqs). 

测试例如:

time(jidoku([[A1,A2,A3,A4,A5,A6,A7,A8,A9], 
     [B1,B2,B3,B4,B5,B6,B7,B8,B9], 
     [C1,C2,C3,C4,C5,C6,C7,C8,C9], 
     [D1,D2,D3,D4,D5,D6,D7,D8,D9], 
     [E1,E2,E3,E4,E5,E6,E7,E8,E9], 
     [F1,F2,F3,F4,F5,F6,F7,F8,F9], 
     [G1,G2,G3,G4,G5,G6,G7,G8,G9], 
     [H1,H2,H3,H4,H5,H6,H7,H8,H9], 
     [I1,I2,I3,I4,I5,I6,I7,I8,I9]], 
     [<>>><>,<<<>><,<<<><>,<><<><,>>><><,><>><>,<>>><>,<>><><,><<>>>], 
     [<<<><>,><<>>>,<><<><,><<<>>,><><<<,<><><>,<>>>><,><><><,<>><>>])). 

,并没有达成任何结论,在这一点上这一个已经运行在一夜之间,我完全不知道自己什么错误。我期望一些缩放问题,但不是这个比例!

如果有人真的知道他们在做什么可以照亮这一点,那将是一件好事!谢谢!

+2

您应该在使用标签/ 2来使用约束求解器以充分发挥其潜力前发布* all * constraints *。有关更多信息,请参阅[CLP(FD)手册](http://www.swi-prolog.org/man/clpfd.html),特别是第A.8.7节:_Enumeration谓词和search_。目前,你只使用“生成和测试”,没有太多的约束传播。 – mat

+0

感谢您的回复!我试图将标签移动到打印之前,但是check_ineqs抱怨的参数没有充分实例化。 – liesb

+2

只需使用CLP(FD)约束'(#<)/ 2'和'(#>)/ 2'而不是更原始的算术函数。这在手册中也有例子解释。提示:'atom_chars(Atom,Cs),maplist(primitive_declarative,Cs,Ds),...'。 – mat

回答

3

这里是你的代码,我脑子里想的版本(其它谓词保持不变):

ineqs(Cells, Ineq) :- 
     atom_chars(Ineq, Cs), 
     maplist(primitive_declarative, Cs, Ds), 
     ineqs_(Ds, Cells). 

ineqs_([], _). 
ineqs_([Op1,Op2|Ops], [A,B,C|Cells]) :- 
     call(Op1, A, B), 
     call(Op2, B, C), 
     ineqs_(Ops, Cells). 

primitive_declarative(<, #<). 
primitive_declarative(>, #>). 

注意,它不会做代码正义的普遍性调用谓词“check_...”,因为谓语陈述什么持有并能在几个方向可以使用:是的,它可如果约束抱来检查,但它可以用来指出制约必须搁置了一些变量。因此,我避免了使用命令,并使用更多的声明性名称。

您在jidoku/3使用ineqs/2有:maplist(ineqs, Rows, RowsIneqs)

你的榜样,并与新版本的结果,使用SWI 7.3。2:

?- length(Rows, 9), maplist(same_length(Rows), Rows), 
    time(jidoku(Rows, 
    [<>>><>,<<<>><,<<<><>,<><<><,>>><><,><>><>,<>>><>,<>><><,><<>>>], 
    [<<<><>,><<>>>,<><<><,><<<>>,><><<<,<><><>,<>>>><,><><><,<>><>>])), 
    maplist(writeln, Rows). 
% 2,745,471 inferences, 0.426 CPU in 0.432 seconds (99% CPU, 6442046 Lips) 
[1,5,4,8,7,2,6,9,3] 
[2,3,9,1,6,5,7,4,8] 
[6,7,8,3,9,4,2,5,1] 
[3,4,1,2,5,6,8,7,9] 
[9,6,5,7,1,8,3,2,4] 
[8,2,7,9,4,3,1,6,5] 
[4,9,3,6,2,1,5,8,7] 
[7,8,2,5,3,9,4,1,6] 
[5,1,6,4,8,7,9,3,2] 
Rows = [[1, 5, 4, 8, 7, 2, 6, 9|...], ...]. 

事实上,注意没有在所有标签需要计算在这种特殊情况下的独特的解决方案,因为约束求解器是强大到足以降低所有域独居套。

在你之前的版本中,所有的时间都被不必要地浪费在生成排列上,最终被认为是不一致的。使用新版本,约束求解器有机会早些时候应用这些知识。

因此,建议先声明所有约束,然后才调用labeling/2来搜索具体解决方案,如CLP(FD) manual中所述。

+2

谢谢!能看到这样美丽的代码真是太好了,我一定会阅读你以前从未听说过的所有东西。感谢让我的宝宝Prolog减少一点痛苦! – liesb

+1

太棒了!请注意,我使用的所有语言元素都已经出现在您的原始代码段中。最后,关于你的另一个问题:“当我没有指定每行有4个元素时,为什么需要更长的解算器?”您怀疑它也会尝试在这种情况下探索更小/更大的行是绝对正确的。此外,它不公平地*并且将永远不会在没有你的指导的情况下达到这个特定难题所需的布局。查看目标答案??append([A,B,C,D],Ls).',它出现在你的代码片段中。通常情况下,它更多的是*终止*速度。 – mat