如何优化我的数独求解器?

时间:2014-09-07 10:40:07

标签: prolog

所以我一直在努力编写一个我写过的数独求解器:

permutation([], []).
permutation([X|L], P) :-
   permutation(L, L1),
   insert(X, L1, P).

del(X, [X|Xs], Xs).
del(X, [Y|Ys], [Y|Zs]) :-
   del(X, Ys, Zs).

insert(X, List, BiggerList) :-
   del(X, BiggerList, List).

block(X1,X2,X3,X4,X5,X6,X7,X8,X9) :-
   permutation([1,2,3,4,5,6,7,8,9],[X1,X2,X3,X4,X5,X6,X7,X8,X9]).

solveSudoku(X11,X12,X13,X14,X15,X16,X17,X18,X19,X21,X22,X23,X24,X25,X26,X27,X28,X29,X31,X32,X33,X34,X35,X36,X37,X38,X39,X41,X42,X43,X44,X45,X46,X47,X48,X49,X51,X52,X53,X54,X55,X56,X57,X58,X59,X61,X62,X63,X64,X65,X66,X67,X68,X69,X71,X72,X73,X74,X75,X76,X77,X78,X79,X81,X82,X83,X84,X85,X86,X87,X88,X89,X91,X92,X93,X94,X95,X96,X97,X98,X99) :- 
block(X11,X12,X13,X14,X15,X16,X17,X18,X19) ,
block(X21,X22,X23,X24,X25,X26,X27,X28,X29) ,
block(X31,X32,X33,X34,X35,X36,X37,X38,X39) ,
block(X41,X42,X43,X44,X45,X46,X47,X48,X49) ,
block(X51,X52,X53,X54,X55,X56,X57,X58,X59) ,
block(X61,X62,X63,X64,X65,X66,X67,X68,X69) ,
block(X71,X72,X73,X74,X75,X76,X77,X78,X79) ,
block(X81,X82,X83,X84,X85,X86,X87,X88,X89) ,
block(X91,X92,X93,X94,X95,X96,X97,X98,X99) ,
... 27 blockes

唯一的问题是,对于正常输入它永远不会完成(花费很多时间),我该如何优化它? 它似乎工作,因为当我复制它为4x4它运作良好。对于在开头(行)进行审核的失败案例,它返回false。

the full code Or in another way

1 个答案:

答案 0 :(得分:3)

正如您所观察到的,您的程序可以在较小的问题实例中正常工作,例如: 4×4。你看到的是搜索空间的组合爆炸。要查看差异,请将4x4变量与4个值(4 ^ 16 = 4.29e + 9种组合)与9x9变量进行比较,每个变量9个值(9 ^ 81 = 1.97e + 77种组合)。

你的block / 9谓词的前9个调用构建了一个深度为81级的搜索树,同时只确保“连续没有重复”约束。以下18次调用block / 9检查“列”和“阻止”约束,并在每次发现违规时强制回溯到巨大的搜索树中。这是没有希望的。

改善此行为的方法是在将变量设置为后立即检查为新值,即所有约束仍然可以满足。这实际上是约束逻辑编程中的关键技术之一。几个Prolog系统支持相应的扩展(参见例如dif / 2谓词或alldifferent / 1约束)。

但是,我想在这里展示标准Prolog中实现相同想法的程序。虽然它以一种蛮力的方式这样做,但它仍然非常有效:

?- sudoku.
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[4, 5, 6, 7, 8, 9, 1, 2, 3]
[7, 8, 9, 1, 2, 3, 4, 5, 6]
[2, 1, 4, 3, 6, 5, 8, 9, 7]
[3, 6, 5, 8, 9, 7, 2, 1, 4]
[8, 9, 7, 2, 1, 4, 3, 6, 5]
[5, 3, 1, 6, 4, 2, 9, 7, 8]
[6, 4, 2, 9, 7, 8, 5, 3, 1]
[9, 7, 8, 5, 3, 1, 6, 4, 2]
Yes (0.08s cpu, solution 1, maybe more)

代码由谓词检查/ 1组成,它确保当前变量赋值不会违反任何数独约束。每次将值赋给变量时,都会通过checked_between / 4调用此检查。

sudoku :-
    Grid = [X11,X12,X13,X14,X15,X16,X17,X18,X19,
            X21,X22,X23,X24,X25,X26,X27,X28,X29,
            X31,X32,X33,X34,X35,X36,X37,X38,X39,
            X41,X42,X43,X44,X45,X46,X47,X48,X49,
            X51,X52,X53,X54,X55,X56,X57,X58,X59,
            X61,X62,X63,X64,X65,X66,X67,X68,X69,
            X71,X72,X73,X74,X75,X76,X77,X78,X79,
            X81,X82,X83,X84,X85,X86,X87,X88,X89,
            X91,X92,X93,X94,X95,X96,X97,X98,X99],

    checked_between(Grid, 1, 9, check(Grid)),

    write([X11,X12,X13,X14,X15,X16,X17,X18,X19]), nl,
    write([X21,X22,X23,X24,X25,X26,X27,X28,X29]), nl,
    write([X31,X32,X33,X34,X35,X36,X37,X38,X39]), nl,
    write([X41,X42,X43,X44,X45,X46,X47,X48,X49]), nl,
    write([X51,X52,X53,X54,X55,X56,X57,X58,X59]), nl,
    write([X61,X62,X63,X64,X65,X66,X67,X68,X69]), nl,
    write([X71,X72,X73,X74,X75,X76,X77,X78,X79]), nl,
    write([X81,X82,X83,X84,X85,X86,X87,X88,X89]), nl,
    write([X91,X92,X93,X94,X95,X96,X97,X98,X99]), nl.


% check whether any of the values chosen so far violate a sudoku constraint
check([ X11,X12,X13,X14,X15,X16,X17,X18,X19,
        X21,X22,X23,X24,X25,X26,X27,X28,X29,
        X31,X32,X33,X34,X35,X36,X37,X38,X39,
        X41,X42,X43,X44,X45,X46,X47,X48,X49,
        X51,X52,X53,X54,X55,X56,X57,X58,X59,
        X61,X62,X63,X64,X65,X66,X67,X68,X69,
        X71,X72,X73,X74,X75,X76,X77,X78,X79,
        X81,X82,X83,X84,X85,X86,X87,X88,X89,
        X91,X92,X93,X94,X95,X96,X97,X98,X99]) :-

    nodups([X11,X12,X13,X14,X15,X16,X17,X18,X19]),
    nodups([X21,X22,X23,X24,X25,X26,X27,X28,X29]),
    nodups([X31,X32,X33,X34,X35,X36,X37,X38,X39]),
    nodups([X41,X42,X43,X44,X45,X46,X47,X48,X49]),
    nodups([X51,X52,X53,X54,X55,X56,X57,X58,X59]),
    nodups([X61,X62,X63,X64,X65,X66,X67,X68,X69]),
    nodups([X71,X72,X73,X74,X75,X76,X77,X78,X79]),
    nodups([X81,X82,X83,X84,X85,X86,X87,X88,X89]),
    nodups([X91,X92,X93,X94,X95,X96,X97,X98,X99]),

    nodups([X11,X21,X31,X41,X51,X61,X71,X81,X91]),
    nodups([X12,X22,X32,X42,X52,X62,X72,X82,X92]),
    nodups([X13,X23,X33,X43,X53,X63,X73,X83,X93]),
    nodups([X14,X24,X34,X44,X54,X64,X74,X84,X94]),
    nodups([X15,X25,X35,X45,X55,X65,X75,X85,X95]),
    nodups([X16,X26,X36,X46,X56,X66,X76,X86,X96]),
    nodups([X17,X27,X37,X47,X57,X67,X77,X87,X97]),
    nodups([X18,X28,X38,X48,X58,X68,X78,X88,X98]),
    nodups([X19,X29,X39,X49,X59,X69,X79,X89,X99]),

    nodups([X11,X12,X13,X21,X22,X23,X31,X32,X33]),
    nodups([X41,X42,X43,X51,X52,X53,X61,X62,X63]),
    nodups([X71,X72,X73,X81,X82,X83,X91,X92,X93]),
    nodups([X14,X15,X16,X24,X25,X26,X34,X35,X36]),
    nodups([X44,X45,X46,X54,X55,X56,X64,X65,X66]),
    nodups([X74,X75,X76,X84,X85,X86,X94,X95,X96]),
    nodups([X17,X18,X19,X27,X28,X29,X37,X38,X39]),
    nodups([X47,X48,X49,X57,X58,X59,X67,X68,X69]),
    nodups([X77,X78,X79,X87,X88,X89,X97,X98,X99]).


nodups([]).
nodups([X|Xs]) :-
    not_contains(Xs, X),
    nodups(Xs).

not_contains([], _).
not_contains([Y|Ys], X) :-
    X \== Y,
    not_contains(Ys, X).

checked_between([], _, _, _).
checked_between([X|Xs], L, H, Check) :-
    between(L, H, X),
    call(Check),
    checked_between(Xs, L, H, Check).

between(L, H, L) :- L =< H.
between(L, H, X) :-
    L < H,
    L1 is L+1,
    between(L1, H, X).