prolog程序中没有输出:clp(fd)太慢了

时间:2015-04-23 16:16:54

标签: performance prolog clpfd

我是Prolog编程的新手,我写了一个代码来解决4个4个魔方,但是当我运行程序时,程序没有给出任何输出;它只是继续执行(忙),最终我不得不退出SWI-Prolog。请指导我这个问题。

代码:

:- use_module(library(clpfd)).

magic_square(Puzzle,Sum) :-
    Puzzle = [S11,S12,S13,S14,
              S21,S22,S23,S24,
              S31,S32,S33,S34,
              S41,S42,S43,S44],

    Puzzle ins 1..16,
    all_different(Puzzle),
    labeling([],Puzzle),

    R1 = [S11,S12,S13,S14],           % rows
    R2 = [S21,S22,S23,S24],
    R3 = [S31,S32,S33,S34],
    R4 = [S41,S42,S43,S44],

    C1 = [S11,S21,S31,S41],           % columns
    C2 = [S12,S22,S32,S42],
    C3 = [S13,S23,S33,S43],
    C4 = [S14,S24,S34,S44],

    Diag1 = [S11,S22,S33,S44],        % diagonals
    Diag2 = [S14,S23,S32,S41],

    S11 + S12 + S13 + S14 #= Sum,     % rows
    S21 + S22 + S23 + S24 #= Sum,
    S31 + S32 + S33 + S34 #= Sum,
    S41 + S42 + S43 + S44 #= Sum,

    S11 + S21 + S31 + S41 #= Sum,     % columns
    S12 + S22 + S32 + S42 #= Sum,
    S13 + S23 + S33 + S43 #= Sum,
    S14 + S24 + S34 + S44 #= Sum,

    S11 + S22 + S33 + S44 #= Sum,     % diagonals
    S14 + S23 + S32 + S41 #= Sum.

我尝试将sum变量更改为64,但仍然没有任何反应。我正在使用SWI-Prolog。

1 个答案:

答案 0 :(得分:3)

首先要做的事情!

使用clp(fd),始终推迟labeling/2之类的枚举目标,直到所有约束都被陈述为止。一般来说,良好做法按以下方式进行:

  1. 声明所有使用的变量
  2. 的初始域
  3. 陈述应通过约束保存的所有关系
  4. 触发搜索以获取labeling/2或其他有限域枚举目标的解决方案
  5. 在评论中你注意到同样适用于3x3魔方;当然,但它的工作不是因为的标签和约束发布的反向排序 - 它尽管仍然有效

    你在合理的时间内得到解决方案的原因是找到大小为3x3 的魔术方块比找到 4x4 更容易数量级

    重新排序谓词magicSquare4x4_withSum/2中的目标,如下所示:

    :- use_module(library(clpfd)).
    
    magicSquare4x4_withSum(Zs,Sum) :-
        Zs = [S11,S12,S13,S14,
              S21,S22,S23,S24,
              S31,S32,S33,S34,
              S41,S42,S43,S44], 
        Zs ins 1..16,                   % state the initial finite domain
        S11 + S12 + S13 + S14 #= Sum,   % rows
        S21 + S22 + S23 + S24 #= Sum,
        S31 + S32 + S33 + S34 #= Sum,
        S41 + S42 + S43 + S44 #= Sum,    
        S11 + S21 + S31 + S41 #= Sum,   % columns
        S12 + S22 + S32 + S42 #= Sum,
        S13 + S23 + S33 + S43 #= Sum,
        S14 + S24 + S34 + S44 #= Sum,
        S11 + S22 + S33 + S44 #= Sum,   % diagonals
        S14 + S23 + S32 + S41 #= Sum,   
        all_different(Zs).              % no two variables shall have the same value
    

    magicSquare4x4_withSum/2的最一般查询在很短的时间内确定性地成功;但是,给出的答案并未显示某些4x4魔方的具体值;相反,它呈现了解决方案必须遵守的待决约束,以便存在。

    ?- time(magicSquare4x4_withSum(Zs,Sum)).
    % 7,780 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 8396967 Lips)
    Zs = [_G9481, _G9484, _G9487, _G9490, _G9493, _G9496, _G9499, _G9502|...],
    _G9481 in 1..16,
    all_different([_G9481, _G9484, _G9487, _G9490, _G9493, _G9496, _G9499|...]),
    _G9481+_G9496+_G9511+_G9526#=Sum,
    _G9481+_G9493+_G9505+_G9517#=Sum,
    _G9481+_G9484+_G9487+_G9490#=Sum,
    _G9484 in 1..16,
    _G9484+_G9496+_G9508+_G9520#=Sum,
    _G9487 in 1..16,
    _G9487+_G9499+_G9511+_G9523#=Sum,
    _G9490 in 1..16,
    _G9490+_G9499+_G9508+_G9517#=Sum,
    _G9490+_G9502+_G9514+_G9526#=Sum,
    _G9493 in 1..16,
    _G9493+_G9496+_G9499+_G9502#=Sum,
    _G9496 in 1..16,
    _G9499 in 1..16,
    _G9502 in 1..16,
    _G9505 in 1..16,
    _G9505+_G9508+_G9511+_G9514#=Sum,
    _G9508 in 1..16,
    _G9511 in 1..16,
    _G9514 in 1..16,
    _G9517 in 1..16,
    _G9517+_G9520+_G9523+_G9526#=Sum,
    _G9520 in 1..16,
    _G9523 in 1..16,
    _G9526 in 1..16,
    Sum in 4..64.
    

    现在,让我们开始吧!

    ?- time((magicSquare4x4_withSum(Zs,Sum), labeling([],Zs))).
    % 8,936,459 inferences, 1.025 CPU in 1.025 seconds (100% CPU, 8720473 Lips)
    Zs = [1, 2, 15, 16, 12, 14, 3, 5, 13|...],
    Sum = 34 ;
    % 37,098 inferences, 0.006 CPU in 0.006 seconds (100% CPU, 6073990 Lips)
    Zs = [1, 2, 15, 16, 13, 14, 3, 4, 12|...],
    Sum = 34                                    % and the search goes on...
    

    我们可以做些什么来加速搜索?首先,我们尝试不同的标记启发式/选项。

    ?- time((magicSquare4x4_withSum(Zs,Sum), labeling([ff],Zs))).
    % 5,056,298 inferences, 0.578 CPU in 0.578 seconds (100% CPU, 8749040 Lips)
    Zs = [1, 2, 15, 16, 12, 14, 3, 5, 13|...],
    Sum = 34 ;
    % 36,783 inferences, 0.006 CPU in 0.006 seconds (100% CPU, 5733914 Lips)
    Zs = [1, 2, 15, 16, 13, 14, 3, 4, 12|...],
    Sum = 34                                    % and the search goes on...
    

    我们还能做些什么?通过提供总和作为常量来帮助搜索。

    ?- time((magicSquare4x4_withSum(Zs,34), labeling([],Zs))).
    % 106,296 inferences, 0.017 CPU in 0.017 seconds (100% CPU, 6242045 Lips)
    Zs = [1, 2, 15, 16, 12, 14, 3, 5, 13|...] ;
    % 36,858 inferences, 0.009 CPU in 0.009 seconds (100% CPU, 4076658 Lips)
    Zs = [1, 2, 15, 16, 13, 14, 3, 4, 12|...] ;
    % 209,206 inferences, 0.028 CPU in 0.028 seconds (100% CPU, 7430044 Lips)
    Zs = [1, 2, 16, 15, 13, 14, 4, 3, 12|...]   % and the search goes on...
    

    在性能方面,a-priori将总和约束为值34具有大量效果!

    编辑2015-04-24

    我们还能做些什么?

    我们可以添加限制搜索和解决方案空间的其他约束,排除仅仅是"翻转"或"轮换"彼此的。

    让我们考虑一下大小为3x3的魔方:有8个不同的,但实际上它们都是"翻转" /"旋转"相同的魔方。 在这8个中,我们可以在知道如何构造它们时安全地消除7个。

    破坏对称性限制了大小 无论是搜索空间还是解空间,我们都可以通过使用魔方四角之间的排序约束来表达它:

    magicSquare4x4withRestrictedSymmetries(Zs) :-
        Zs = [S11,_,_,S14,
                _,_,_,_,
                _,_,_,_,
              S41,_,_,S44], 
        S11 #< S14,
        S11 #< S44,
        S11 #< S41,
        S14 #< S41.
    

    这些额外的约束可能会或可能不会帮助我们更快地找到第一个解决方案,但是当搜索 all时,他们肯定有帮助溶液

    首先,让我们在没有破坏对称性的附加约束的情况下这样做:

    ?- time((findall(t,(magicSquare4x4_withSum(Zs,34),
                        labeling([ff],Zs)),Ts),
             length(Ts,N_Sols))).
    % 1,526,766,108 inferences, 152.1 CPU in 152.1 seconds (100% CPU, 10033768 Lips)
    Ts = [t, t, t, t, t, t, t, t, t|...],
    N_Sols = 7040.
    

    现在,有了额外的限制:

    ?- time((findall(t,(magicSquare4x4_withSum(Zs,34),
                        magicSquare4x4withRestrictedSymmetries(Zs),
                        labeling([ff],Zs)),Ts),
             length(Ts,N_Sols))).
    % 129,527,384 inferences, 12.580 CPU in 12.578 seconds (100% CPU, 10295893 Lips)
    Ts = [t, t, t, t, t, t, t, t, t|...],
    N_Sols = 880.
    

    大赢!对所有解决方案的搜索速度提高了10倍以上。正如所料,当破坏对称性时,解决方案的数量精确地除以8(880 * 8 = 7040)。 HTH!