Prolog:组合谓词失败

Prolog: combined predicate failed

作为初学者,我正在尝试解决矩阵问题,其中解决方案是矩阵中不同数字的求和或乘法。例如,[[14,_,_,_],[15,_,_,_],[28,_,1,_]]。谓词应根据矩阵生成解决方案。然而,我遇到了一个问题,我的组合谓词失败了,但每个谓词都独立成功了。

我把问题分解为求和和乘法。因此,使用 clpfd 库和一些谓词进行求和:

removeHead([Head|Tail],Tail).

get_head_element([],_).
get_head_element([Head|Rest],Head).

solve_sum(Row,RestRow):-
    get_head_element(Row, Goal),
    removeHead(Row,RestRow),
    RestRow ins 1..9,
    all_distinct(RestRow),
    sum(RestRow, #=, Goal).

乘法:

multiply(List,Result):-
    multiply(List,1,Result).

multiply([Element|RestList],PrevResult,Result):-
    NextResult #= PrevResult * Element,
    multiply(RestList,NextResult, Result).

multiply([], Result, Result).

solve_multiply(Row,RestRow):-
    get_head_element(Row, Goal),
    removeHead(Row,RestRow),
    RestRow ins 1..9,
    multiply(RestRow,Goal),
    all_distinct(RestRow).

solve_sumsolve_multiply 都适用于一行,但在这里我将这两个谓词组合为:

solve_row_sum_or_multiply([],_).

solve_row_sum_or_multiply([HeadRow|Matrix],Solution):-
maplist(all_distinct,Matrix),
get_head_element(HeadRow,Goal),
( Goal >= 25
-> solve_multiply(HeadRow,Solution),
   write(Solution)
; ( solve_sum(HeadRow,Solution),
    write(Solution)
    ; solve_multiply(HeadRow,Solution),
    write(Solution))
),solve_row_sum_or_multiply(Matrix,Solution).

当我使用solve_row_sum_or_multiply([[14,_,_,_],[15,_,_,_],[28,_,_,_]],X)时,它会给我可能的组合。然而,当 solve_row_sum_or_multiply([[14,_,_,_],[15,_,_,_],[28,_,1,_]],X) 时,它给了我错误,但那里有一个可能的解决方案,例如 [[14,7,2,1],[15,3,7,5],[28,4,1,7]].

我想知道哪里出了问题。非常感谢任何帮助!

编辑

问题在组合谓词内部,最好编写答案建议的谓词而不是嵌套的 if-then-else 谓词。

让我们回顾一下这段代码。

介绍码

:- use_module(library(clpfd)).

% ---
% Make sure you see all the information in a list printed at the 
% toplevel, i.e. tell the toplevel to not elide large lists (print
% them in full, don't use '...')
% ---

my_print :-
   Depth=100,
   Options=[quoted(true), portray(true), max_depth(Depth), attributes(portray)],
   set_prolog_flag(answer_write_options,Options),
   set_prolog_flag(debugger_write_options,Options).
   
:- my_print.   

multiply/2

重新编码 multiply/2 谓词,以便更容易阅读。添加测试代码以确保它确实按照我们的想法执行:

% ---
% Set up constraint: all the elements in List, multiplied
% together, must yield Result. If the List is empty, the
% Result must be 1.
% ---

multiply(Factors,Product):-
    multiply_2(Factors,1,Product).

multiply_2([Factor|Factors],PartialProduct,Product):-
    PartialProduct2 #= PartialProduct * Factor,
    multiply_2(Factors,PartialProduct2,Product).

multiply_2([],Product,Product).

solve_multiply([Product|Factors]):-
    Factors ins 1..9,
    multiply(Factors,Product),
    all_distinct(Factors).
    
% ---
% Testing
% ---

:- begin_tests(multiply).

test(1) :- 
   multiply([X,Y,Z],6),
   [X,Y,Z] ins 1..9,
   X #=< Y, Y #=< Z, 
   bagof([X,Y,Z],label([X,Y,Z]),Bag),
   sort(Bag,BagS),
   assertion(BagS == [[1, 1, 6], [1, 2, 3]]).

test(2) :- 
   multiply([],Result),
   assertion(Result == 1).

test(3) :- 
   multiply([X,Y],3),
   [X,Y] ins 1..9,
   X #=< Y, 
   bagof([X,Y],label([X,Y]),Bag),
   sort(Bag,BagS),
   assertion(BagS == [[1,3]]).

test(4) :-
   solve_multiply([6,X,Y,Z]),
   bagof([X,Y,Z],label([X,Y,Z]),Bag),
   sort(Bag,BagS),
   assertion(BagS == [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]).
   
test(5) :-
   solve_multiply([362880,F1,F2,F3,F4,F5,F6,F7,F8,F9]),
   F1 #=< F2,
   F2 #=< F3,
   F3 #=< F4,
   F4 #=< F5,
   F5 #=< F6,
   F6 #=< F7,
   F7 #=< F8,
   F8 #=< F9,
   bagof([F1,F2,F3,F4,F5,F6,F7,F8,F9],label([F1,F2,F3,F4,F5,F6,F7,F8,F9]),Bag),
   sort(Bag,BagS),
   assertion(BagS == [[1,2,3,4,5,6,7,8,9]]).

test(6,fail) :-
   solve_multiply([-1,X,Y,Z]).
   
:- end_tests(multiply).

solve_sum/1

总而言之,在简化和重新编码为 Prolog 的“模式匹配”风格而不是(因为想要一个更好的词)原始问题中使用的“Algol 调用风格”之后(这让我很困惑我不得不说)。

这看起来也不错。请注意,谓词 solve_sum/1multiply/2 的论证风格完全不同 如果保持相同的调用约定,reader(和自己)会更好 - 也更容易。

solve_sum([Sum|Terms]):-
    Terms ins 1..9,
    all_distinct(Terms),
    sum(Terms, #=, Sum).
    
% ---
% Testing
% ---

:- begin_tests(sum).

test(0) :- 
   solve_sum([0]).
   
test(1) :- 
   solve_sum([6,X,Y,Z]),
   X #=< Y, Y #=< Z, 
   bagof([X,Y,Z],label([X,Y,Z]),Bag),
   sort(Bag,BagS),
   assertion(BagS == [[1, 2, 3]]).

test(2) :- 
   solve_sum([9,X,Y,Z]),
   X #=< Y, Y #=< Z, 
   bagof([X,Y,Z],label([X,Y,Z]),Bag),
   sort(Bag,BagS),
   assertion(BagS == [[1,2,6],[1,3,5],[2,3,4]]).

test(3,fail) :- 
   solve_sum([1,_X,_Y,_Z]).
   
:- end_tests(sum).   

获得好的结果的打印谓词

Prolog 可以轻松地“即时”构建任意结构,只需准确规定结构的外观即可。

我们的“解决方案”是:

  • 一个列表
  • 列表中的每个元素都有一个“行”(代表其中一个表达式)
  • 行中有一项:row(Result,Op,RowValues) 其中 Result 是等式的左边,Opadd 或 [=24 之一=] 和 RowValues 是一个列表,其中包含为方程中的值找到的值。
print_solution([]).

print_solution([Labeling|Labelings]) :-
   format("---~n"),
   print_rows_of_solution(Labeling),
   format("---~n"),
   print_solution(Labelings).

print_rows_of_solution([]).

print_rows_of_solution([row(Result,Op,RowValues)|Rows]) :-
   print_row(Result,RowValues,Op),
   print_rows_of_solution(Rows).

print_row(Result,[RowEntry|RowEntries],Op) :-
   format("~q = ~q",[Result,RowEntry]),
   print_row_2(RowEntries,Op).
   
print_row_2([RowEntry|RowEntries],Op) :-
   ((Op == mult) -> OpChar = '*'
   ; (Op == add)  -> OpChar = '+'
   ; OpChar = '?'),
   format(" ~q ~q",[OpChar,RowEntry]),
   print_row_2(RowEntries,Op).
   
print_row_2([],_) :-
   format("~n").

solve_row_sum_or_multiply/2

这已被修改为还收集在第二个参数(列表)中选择的操作。

请注意,“矩阵上的 maplist-all-distinct”没有在这里完成,因为这似乎是错误的。

solve_row_sum_or_multiply([],[]).

solve_row_sum_or_multiply([Row|MoreRows],[mult|Ops]) :-
   Row = [X|_Xs],
   X >= 25,   
   solve_multiply(Row), % we now have imposed a product constraint on the current Row
   solve_row_sum_or_multiply(MoreRows,Ops).

solve_row_sum_or_multiply([Row|MoreRows],[add|Ops]) :-
   Row = [X|_Xs],
   X < 25,
   solve_sum(Row), % we now have imposed a sum constraint on the current Row
   solve_row_sum_or_multiply(MoreRows,Ops).
 
solve_row_sum_or_multiply([Row|MoreRows],[mult|Ops]) :-
   Row = [X|_Xs],
   X < 25,
   solve_multiply(Row), % alternatively, we now have imposed a product constraint on the current Row   
   solve_row_sum_or_multiply(MoreRows,Ops).

最后是“主要”谓词

trial_run/1中我们规定除了X32之外的所有变量都是不同的并且X32是1。如果X32在“不同的集合”中,none 的变量可以取值 1 - 在这种情况下没有解决方案。

我们还进行了排序以仅获得一组“规范”解决方案:

我们使用bagof/3:

收集了所有可能的解决方案和标签
trial_run(Solutions) :-
   all_distinct([X11,X12,X13,X21,X22,X23,X31,X33]),
   X32=1,
   X11 #=< X12, X12 #=< X13,
   X21 #=< X22, X22 #=< X23,
   X31 #=< X33,
   % multiple solutions solving x labeling may exist; collect them all
   bagof(
      [
         row(14,Op1,[X11,X12,X13]),
         row(15,Op2,[X21,X22,X23]),
         row(28,Op3,[X31,X32,X33])
      ],
      (
         solve_row_sum_or_multiply( [[14,X11,X12,X13],[15,X21,X22,X23],[28,X31,X32,X33]], [Op1,Op2,Op3] ),
         label([X11,X12,X13,X21,X22,X23,X31,X32,X33])
      ),   
      Solutions).

有用吗?

显然是的,只有一种解决方案:

?- trial_run(Solutions),print_solution(Solutions).
---
14 = 2 + 3 + 9
15 = 1 + 6 + 8
28 = 4 * 1 * 7
---
Solutions = [[row(14,add,[2,3,9]),row(15,add,[1,6,8]),row(28,mult,[4,1,7])]].