从行生成数独'框' - prolog

时间:2012-04-18 14:49:06

标签: prolog sudoku do-while clpfd sublist

我正在尝试编写一个简单的程序,只检查输入数独板是否当前不正确;即它在行,列或“框”中有两个相同的数字。 我没有遇到任何关于行和列部分的麻烦 - 这是一个相当直接的任务,我用下面的代码完成(应该注意'0'代表一个尚未填充的正方形):

:- use_module(library(clpfd)).

%Takes a matrix, determines if any row has repeating numbers
check([H|T]):-
    all_diff(H),
    check(T).
check([]).

%takes a list, checks if it contains repetitions other than '0'.
all_diff([]).
all_diff([X|Xs]) :-
    ( X = 0 ->
    all_diff(Xs)
        ;
        \+memberchk(X, Xs),
      all_diff(Xs)

    ).

consistent(Rows):-
    check(Rows),                %verify rows are free of repeats
    transpose(Rows,Columns),    %L1 represents columns
    check(Columns),         %verify all columns are free of repeats
        [H|T] = Rows,
        length(H,M),
    K is integer(sqrt(M)).     %this will give me dimensions of each box (KxK)

但是,我不太清楚如何生成代表KxK'框'的列表(其中K是行长度的平方根)。 我得到了K的值,我想我想按照将row1划分为K个子列表的方式做一些事情,然后将row2的K个子列表附加到row1的子列表的末尾,直到我到达行(K * K)。

不幸的是,我真的不确定如何实现这一目标?是否有一个我可以使用的BIP,它会按照列表并将其分成长度为Y的X列表来执行某些操作?

否则,任何想法?我知道 tiny 但关于dowhile循环,我想它们可以在这里实现,但我不确定我会怎么做? 非常感谢你的帮助!

1 个答案:

答案 0 :(得分:1)

一种简单而无效的方法可以使用索引算法和循环:

...
    Sq = 3,
    findall(B, (between(1, Sq, R),
            between(1, Sq, C),
            block(M, Sq, R, C, B)), Bs).

cell(M, R,C, V) :-
    nth1(R,M,Row), nth1(C,Row,V).

block(M, Sq, R,C, B) :-
    findall(V, (between(1, Sq, X),
            between(1, Sq, Y),
            I is (R-1) * Sq + X,
            J is (C-1) * Sq + Y,
            cell(M, I, J, V)), B).

documentation库(clpfd)中,有一种更有效的方法,仅限于已知的标准维度。您可以尝试概括该代码。

编辑这是我的测试用例:请注意矩阵是假的,只是让我们很容易理解块的位置。

q(Bs) :-
    M = [[1,2,3,4,5,6,7,8,9],
         [a,b,c,d,_,3,_,8,5],
         [x,y,z,_,2,_,_,_,_],
         [u,v,z,e,t,y,_,_,_],
         [b,b,b,e,t,y,1,_,_],
         [c,c,c,e,t,y,_,_,_],
         [5,_,_,_,_,_,_,7,3],
         [_,_,2,_,1,_,_,_,_],
         [_,_,_,_,4,_,_,_,9]],

    Sq = 3,
    findall(B, (between(1, Sq, R),
            between(1, Sq, C),
            block(M, Sq, R, C, B)), Bs).

cell(M, R,C, V) :-
    nth1(R,M,Row), nth1(C,Row,V).

block(M, Sq, R,C, B) :-
    findall(V, (between(1, Sq, X),
            between(1, Sq, Y),
            I is (R-1) * Sq + X,
            J is (C-1) * Sq + Y,
            cell(M, I, J, V)), B).

并使用此命令进行测试:

?- q(Bs),maplist(writeln,Bs).
[1,2,3,a,b,c,x,y,z]
[4,5,6,d,_G928,3,_G934,2,_G940]
[7,8,9,_G895,8,5,_G904,_G907,_G910]
[u,v,z,b,b,b,c,c,c]
[e,t,y,e,t,y,e,t,y]
[_G796,_G799,_G802,1,_G808,_G811,_G814,_G817,_G820]
[5,_G769,_G772,_G775,_G778,2,_G784,_G787,_G790]
[_G736,_G739,_G742,_G745,1,_G751,_G754,4,_G760]
[_G706,7,3,_G715,_G718,_G721,_G724,_G727,9]
Bs = [[1, 2, 3, a, b, c, x, y|...], [4, 5, 6, d, _G928, 3, _G934|...], [7, 8, 9, _G895, 8, 5|...], [u, v, z, b, b|...], [e, t, y, e|...], [_G796, _G799, _G802|...], [5, _G769|...], [_G736|...], [...|...]].