Prolog:组合谓词失败



作为一名初学者,我试图解决一个矩阵问题,其中的解决方案是矩阵中不同数字的求和或相乘。例如,[[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.   

乘/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/1在自变量风格上与multiply/2大不相同。如果保持相同的调用约定,对读者(和自己(来说会更好,也更容易。

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使构建任意结构变得容易;在飞行中";,人们只需要确切地规定结构的外观。

我们的";解决方案";是:

  • 列表
  • 对于列表的每个元素;行";(代表其中一个表达(
  • 行a项:row(Result,Op,RowValues),其中Result是方程的左手边,Opaddmult之一,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

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

注意;在矩阵上所有不同的映射列表";这里没有,因为这似乎是错误的。

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。如果CCD_;不同集合";,没有一个变量可以取值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])]].

最新更新