简体   繁体   English

Prolog数字列表的总和

[英]Prolog sum of a list of numbers

I am new to Prolog and I want to write poppler(Nums, Plate, Tastiness) that takes a list of exactly 9 numbers as input, and, if possible, returns a permutation of those numbers that forms a delicious poppler plate when Plate is read in row-major format. 我是Prolog的新手,我想写一个poppler(Nums, Plate, Tastiness) ,它将9个数字的列表作为输入,如果可能的话,返回那些在读取Plate时形成美味的poppler板的数字的排列以行主格式。

A Poppler plate is said to be delicious if the sum of the Popplers in each of the three rows, columns, and two main diagonals is the same. 如果三排,两列和两条主要对角线中的每一个的Popplers的总和相同,则称Poppler板是美味的。 This common sum is called its tastiness. 这个共同的总和被称为它的美味。

For example, this is a delicious Poppler plate with tastiness 15 : 例如,这是一个美味的Poppler板,味道15

2 7 6

9 5 1

4 3 8

Here is my try: 这是我的尝试:

size([], 0).
size([Head|T], N) :-
   size(T, N1),
   N is N1+1.

is_equal([U, V, W], [X, Y, Z], Sum) :-
    Sum is U + V + W,
    Sum is X + Y + Z.

poppler(Nums, Plate, Tastiness):- 
    size(Nums, 9),
    poppler(Nums, [A, B, C, D, E, F, G, H, I], Tastiness),
    member(A, Nums),
    member(B, Nums),
    member(C, Nums),
    member(D, Nums),
    member(E, Nums),
    member(F, Nums),
    member(G, Nums),
    member(H, Nums),
    member(I, Nums),
    is_equal([A, B, C], [D, E, F], Tastiness),
    is_equal([A, B, C], [G, H, I], Tastiness),
    is_equal([G, H, I], [D, E, F], Tastiness),
    is_equal([A, D, G], [B, E, H], Tastiness),
    is_equal([A, D, G], [C, F, I], Tastiness),
    is_equal([B, E, H], [C, F, I], Tastiness),
    is_equal([A, E, I], [C, E, G], Tastiness).

But this doesn't work. 但这不起作用。 How can I fix it? 我该如何解决?

Here is a fixed version of your code with some comments. 这是您的代码的固定版本,带有一些注释。 Tested in SWI-Prolog. 在SWI-Prolog中测试过。

It works, but it's really slow (will work for minutes for your example). 它可以工作,但它确实很慢(对于你的例子,它会工作几分钟)。 This is because the search space is large, and there is no search space pruning. 这是因为搜索空间很大,并且没有搜索空间修剪。

Your should really use constraint programming approach for this problem - it prunes search space in a clever way, and that program works instantly. 你应该真正使用约束编程方法解决这个问题 - 它以一种聪明的方式修剪搜索空间,并且该程序可以立即工作。

% should really just use length/2
size([], 0).
size([Head|T],N) :- size(T,N1), N is N1+1.

% could use simpler version of this like "is_equal([X, Y, Z], Sum)"
is_equal([U, V, W], [X, Y, Z], Sum) :- Sum is U + V + W, Sum is X + Y + Z.

poppler(Nums, Plate, Tastiness) :- 
    size(Nums, 9),
    [A, B, C, D, E, F, G, H, I] = Plate,

    msort(Nums, Sorted),

    member(A, Nums),
    member(B, Nums),
    member(C, Nums),
    member(D, Nums),
    member(E, Nums),
    member(F, Nums),
    member(G, Nums),
    member(H, Nums),
    member(I, Nums),

    % Check if Plate is a permutation of Nums
    msort(Plate, Sorted),

    is_equal([A, B, C], [D, E, F], Tastiness),
    is_equal([A, B, C], [G, H, I], Tastiness),
    is_equal([G, H, I], [D, E, F], Tastiness),
    is_equal([A, D, G], [B, E, H], Tastiness),
    is_equal([A, D, G], [C, F, I], Tastiness),
    is_equal([B, E, H], [C, F, I], Tastiness),
    is_equal([A, E, I], [C, E, G], Tastiness).

Looks like a perfect problem to solve with constraint logic programming. 看起来像是一个用约束逻辑编程解决的完美问题。

Here is my solution in ECLiPSe CLP Prolog (can be translated to other Prolog systems): 这是我在ECLiPSe CLP Prolog中的解决方案(可以翻译成其他Prolog系统):

:- lib(gfd).

poppler(Nums, Plate, S) :-
   [A, B, C, D, E, F, G, H, I] = Plate,
   sorted(Nums, Sorted), sorted(Plate, Sorted),
   % rows
   A + B + C #= S,
   D + E + F #= S,
   G + H + I #= S,
   % colums
   A + D + G #= S,
   B + E + H #= S,
   C + F + I #= S,
   % diagonals
   A + E + I #= S,
   C + E + G #= S,
   labeling(Plate).

Test run: 测试运行:

[eclipse]: poppler([1, 2, 3, 4, 5, 6, 7, 8, 9], Plate, 15).
Plate = [2, 7, 6, 9, 5, 1, 4, 3, 8]

I think your main problem is that using member/2 you are generating much more attempts than will be discarded later. 我认为你的主要问题是使用成员/ 2你生成比稍后将丢弃更多的尝试。 You could instead use permutation/2: 你可以改为使用置换/ 2:

poppler0(Nums, Plate, Tastiness):-
    Plate = [A, B, C, D, E, F, G, H, I],
    permutation(Nums, Plate),
    is_equal([A, B, C], [D, E, F], Tastiness),
    is_equal([A, B, C], [G, H, I], Tastiness),
    is_equal([G, H, I], [D, E, F], Tastiness),
    is_equal([A, D, G], [B, E, H], Tastiness),
    is_equal([A, D, G], [C, F, I], Tastiness),
    is_equal([B, E, H], [C, F, I], Tastiness),
    is_equal([A, E, I], [C, E, G], Tastiness).

yields 产量

?- numlist(1,9,L),poppler0(L,X,15).
L = [1, 2, 3, 4, 5, 6, 7, 8, 9],
X = [2, 7, 6, 9, 5, 1, 4, 3, 8] ;
L = [1, 2, 3, 4, 5, 6, 7, 8, 9],
X = [2, 9, 4, 7, 5, 3, 6, 1, 8] ;
...

Instead of member/3, select/3 would get non duplicated: 而不是member / 3,select / 3将不会重复:

poppler1(Nums, Plate, Tastiness):-
    Plate = [A, B, C, D, E, F, G, H, I],
    %permutation(Nums, Plate),
    select(A, Nums, Num1),
    select(B, Num1, Num2),
    select(C, Num2, Num3),
    select(D, Num3, Num4),
    select(E, Num4, Num5),
    select(F, Num5, Num6),
    select(G, Num6, Num7),
    select(H, Num7, Num8),
    select(I, Num8, []),
    is_equal([A, B, C], [D, E, F], Tastiness),
    is_equal([A, B, C], [G, H, I], Tastiness),
    is_equal([G, H, I], [D, E, F], Tastiness),
    is_equal([A, D, G], [B, E, H], Tastiness),
    is_equal([A, D, G], [C, F, I], Tastiness),
    is_equal([B, E, H], [C, F, I], Tastiness),
    is_equal([A, E, I], [C, E, G], Tastiness).

Also, since the permutation now is 'sliced', we could 'push' some of the tests early, to make the whole faster: 此外,由于排列现在是“切片”,我们可以提前“推”一些测试,以使整体更快:

poppler2(Nums, Plate, Tastiness):-
    Plate = [A, B, C, D, E, F, G, H, I],
    select(A, Nums, Num1),
    select(B, Num1, Num2),
    select(C, Num2, Num3),
    select(D, Num3, Num4),
    select(E, Num4, Num5),
    select(F, Num5, Num6),
    is_equal([A, B, C], [D, E, F], Tastiness),
    select(G, Num6, Num7),
    select(H, Num7, Num8),
    select(I, Num8, []),
    is_equal([A, B, C], [G, H, I], Tastiness),
    is_equal([G, H, I], [D, E, F], Tastiness),
    is_equal([A, D, G], [B, E, H], Tastiness),
    is_equal([A, D, G], [C, F, I], Tastiness),
    is_equal([B, E, H], [C, F, I], Tastiness),
    is_equal([A, E, I], [C, E, G], Tastiness).

?- numlist(1,9,L),time(poppler0(L,X,15)).
% 642,293 inferences, 0.253 CPU in 0.256 seconds (99% CPU, 2540589 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9],
X = [2, 7, 6, 9, 5, 1, 4, 3, 8] 
.

8 ?- numlist(1,9,L),time(poppler1(L,X,15)).
% 385,446 inferences, 0.217 CPU in 0.217 seconds (100% CPU, 1777885 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9],
X = [2, 7, 6, 9, 5, 1, 4, 3, 8] 
.

9 ?- numlist(1,9,L),time(poppler2(L,X,15)).
% 48,409 inferences, 0.029 CPU in 0.029 seconds (100% CPU, 1643812 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9],
X = [2, 7, 6, 9, 5, 1, 4, 3, 8] 

Another minor problem is that some sum is evaluated more than a time, that's probably due to your choice to code the test with is_equal/3. 另一个小问题是,一些总和的评估时间超过了一个时间,这可能是由于您选择使用is_equal / 3对测试进行编码。 I would write instead 我会改写

poppler3(Nums, Plate, Tastiness):-
    Plate = [A, B, C, D, E, F, G, H, I],
    select(A, Nums, Num1),
    select(B, Num1, Num2),
    select(C, Num2, Num3),
    sumlist([A, B, C], Tastiness),
    select(D, Num3, Num4),
    select(E, Num4, Num5),
    select(F, Num5, Num6),
    sumlist([D, E, F], Tastiness),
    select(G, Num6, Num7),
    sumlist([A, D, G], Tastiness),
    sumlist([C, E, G], Tastiness),
    select(H, Num7, Num8),
    sumlist([B, E, H], Tastiness),
    select(I, Num8, []),
    sumlist([G, H, I], Tastiness),
    sumlist([C, F, I], Tastiness),
    sumlist([A, E, I], Tastiness).

that yields 产量

?- numlist(1,9,L),time(poppler3(L,X,15)).
% 14,371 inferences, 0.004 CPU in 0.004 seconds (99% CPU, 3359784 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9],
X = [2, 7, 6, 9, 5, 1, 4, 3, 8] 
.

but again, sumlist/2 is more general than required, and there is further gain to inline the sum: 但同样,sumlist / 2比一般要求更为通用,并且还可以进一步增加内容:

poppler4(Nums, Plate, Tastiness):-
    Plate = [A, B, C, D, E, F, G, H, I],
    select(A, Nums, Num1),
    select(B, Num1, Num2),
    select(C, Num2, Num3),
    A+B+C =:= Tastiness,
    select(D, Num3, Num4),
    select(E, Num4, Num5),
    select(F, Num5, Num6),
    D+E+F =:= Tastiness,
    select(G, Num6, Num7),
    A+D+G =:= Tastiness,
    C+E+G =:= Tastiness,
    select(H, Num7, Num8),
    B+E+H =:= Tastiness,
    select(I, Num8, []),
    G+H+I =:= Tastiness,
    C+F+I =:= Tastiness,
    A+E+I =:= Tastiness.

yields 产量

?- numlist(1,9,L),time(poppler4(L,X,15)).
% 3,394 inferences, 0.002 CPU in 0.002 seconds (100% CPU, 1827856 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9],
X = [2, 7, 6, 9, 5, 1, 4, 3, 8] 
.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM