[英]Partitioning a List in Prolog
我试图创建一个Prolog谓词,在给定一个列表的情况下,可以查看该列表是否可以拆分为两个总和相同的列表。
我有一个工作列表总和谓词,所以我在分区谓词中使用它。 我首先尝试对谓词进行编码,以查看列表的第一个元素是否等于列表其余部分的总和([2,1,1])。 这就是我要面对的情况。
partitionable([X|Y]) :-
sum([X],SUM),
sum([Y],SUM2),
SUM = SUM2.
但是,我收到此错误消息:
ERROR: is/2: Arithmetic: `[]/0' is not a function.
我想在深入研究递归列表的其余部分之前,先使这部分工作,尽管我对此消息的含义感到困惑,因为我没有编写'[]/0' function
。 任何帮助表示赞赏。
要将列表划分为两个不重叠的子序列 ,我们使用list_subseq_subseq/3
:
list_subseq_subseq([] ,[] ,[]).
list_subseq_subseq([X|Xs],[X|Ys],Zs) :-
list_subseq_subseq(Xs,Ys,Zs).
list_subseq_subseq([X|Xs],Ys,[X|Zs]) :-
list_subseq_subseq(Xs,Ys,Zs).
为了执行整数运算,我们使用clpfd :
:- use_module(library(clpfd)).
让我们放在一起! 在以下示例查询中,我们对列表[1,2,3,4,5,6,7]
分区:
?- Xs = [1,2,3,4,5,6,7],
sum(Xs,#=,Total),
Half*2 #= Total,
list_subseq_subseq(Xs,Ys,Zs),
sum(Ys,#=,Half),
sum(Zs,#=,Half).
Xs = [1,2,3,4,5,6,7], Total = 28, Half = 14, Ys = [1,2,4,7], Zs = [3,5,6]
; Xs = [1,2,3,4,5,6,7], Total = 28, Half = 14, Ys = [1,2,5,6], Zs = [3,4,7]
; Xs = [1,2,3,4,5,6,7], Total = 28, Half = 14, Ys = [1,3,4,6], Zs = [2,5,7]
; Xs = [1,2,3,4,5,6,7], Total = 28, Half = 14, Ys = [1,6,7] , Zs = [2,3,4,5]
; Xs = [1,2,3,4,5,6,7], Total = 28, Half = 14, Ys = [2,3,4,5], Zs = [1,6,7]
; Xs = [1,2,3,4,5,6,7], Total = 28, Half = 14, Ys = [2,5,7] , Zs = [1,3,4,6]
; Xs = [1,2,3,4,5,6,7], Total = 28, Half = 14, Ys = [3,4,7] , Zs = [1,2,5,6]
; Xs = [1,2,3,4,5,6,7], Total = 28, Half = 14, Ys = [3,5,6] , Zs = [1,2,4,7]
; false.
我还将为分区问题提供另一种解决方案。 helper谓词有助于削减列表两个列表。 例如,可以将[1,2,3]删减:
[1,2](左侧)和[3](右侧)或
[3](左侧)和[1,2](右侧)。
helper([],[],[],0,0).
helper([X|XS],[X|L],R,SUML,SUMR):-helper(XS,L,R,SUMN,SUMR),SUML is SUMN+X.
helper([X|XS],L,[X|R],SUML,SUMR):-helper(XS,L,R,SUML,SUMN),SUMR is SUMN+X.
partition(S,L,R):-helper(S,L,R,X,X).
输出为:
1 ?- partition([1,2,3,4],L,R).
L = [1, 4],
R = [2, 3] ;
L = [2, 3],
R = [1, 4] ;
false.
它不断变得更好!
对于不确定的分区列表,如果采用正确的元谓词/谓词组合,则无需实现像list_subseq_subseq/3
这样的递归辅助谓词!
在这个答案中,我们使用tpartition/4
作为元谓词,并使用“通用通配符”谓词(*)/2
作为传递给tpartition/4
的谓词。 (*)/2
可以这样定义:
_ * true.
_ * false.
让我们将tpartition/4
与(*)/2
和clpfd约束(#=)/2
和sum/3
:
?- use_module(library(clpfd)). % use clp(FD) library
true.
?- ABs = [1,2,3,4,5,6,7], % same data as in the earlier answer
sum(ABs,#=,NN),
N*2 #= NN,
tpartition(*,ABs,As,Bs),
sum(As,#=,N),
sum(Bs,#=,N).
NN = 28, N = 14, ABs = [1,2,3,4,5,6,7], As = [1,2,4,7], Bs = [3,5,6]
; NN = 28, N = 14, ABs = [1,2,3,4,5,6,7], As = [1,2,5,6], Bs = [3,4,7]
; NN = 28, N = 14, ABs = [1,2,3,4,5,6,7], As = [1,3,4,6], Bs = [2,5,7]
; NN = 28, N = 14, ABs = [1,2,3,4,5,6,7], As = [1,6,7] , Bs = [2,3,4,5]
; NN = 28, N = 14, ABs = [1,2,3,4,5,6,7], As = [2,3,4,5], Bs = [1,6,7]
; NN = 28, N = 14, ABs = [1,2,3,4,5,6,7], As = [2,5,7] , Bs = [1,3,4,6]
; NN = 28, N = 14, ABs = [1,2,3,4,5,6,7], As = [3,4,7] , Bs = [1,2,5,6]
; NN = 28, N = 14, ABs = [1,2,3,4,5,6,7], As = [3,5,6] , Bs = [1,2,4,7]
; false.
我认为必须将X传递为[X],因为X只是元素(示例中的数字2)。 另一方面,Y本身就是一个列表,不应放在另一个列表中。 这是修改后的版本:
partitionable([X|Y]) :- sum([X],SUM), sum(Y,SUM2), SUM=SUM2.
sum([X|Y],SUM) :- sum(Y, SUBSUM), SUM is SUBSUM + X.
sum([X],SUM) :- X=SUM.
在我的情况下, partitionable([2,1,1])
返回true。
编辑 :由于您不使用is/2
这可能是sum
谓词中的错误。
另外请注意:据我所知,您不需要可partitionable
的解决方案,而是收到的错误消息。 尽管如此,这是我对实现它的看法( 可能是前面的破坏者 ):
/* partitionable(X)
* If a 2-partition of X exists where both sublists have the same sum, then X
* is considered partitionable.
*/
partitionable(X) :- partition(X, A, B), sum(A, SUM), sum(B, SUM2), SUM =:= SUM2, !.
/* partition(X, A, B)
* X is split in two lists A and B and will, during backtracking, bind A and B to
* ALL permutations of the list partition, including permutations for each list.
*/
partition([], [], []).
partition([X|Y], A, B) :- partition(Y, R, B), extract(X, A, R).
partition([X|Y], A, B) :- partition(Y, A, R), extract(X, B, R).
/* extract(X, L, R)
* Extracts exactly one element X from L and unify the result with R.
*/
extract(X, [H|T], R) :- X = H, R = T.
extract(X, [H|T], R) :- R = [H|R2], extract(X, T, R2).
sum([X|Y],SUM) :- sum(Y, SUBSUM), SUM is SUBSUM + X.
sum([X],SUM) :- X = SUM.
也许我在想这个,但是...
如果用“对列表进行分区”的意思是“将列表切成两部分,保留顺序,而不是创建列表的各种排列),那么解决方案似乎应该不会像下面这样复杂:
partitionable( Ns ) :-
append( Prefix , Suffix , Ns ) ,
compute_sum(Prefix,Sum) ,
compute_sum(Suffix,Sum) .
compute_sum( Ns , S ) :- compute_sum(Ns,0,S) .
compute_sum( [] , S , S ) .
compute_sum( [N|Ns] , T , S ) :- T1 is N+T , compute_sum(Ns,T1,S) .
如果您想避免使用内置函数,则可以执行以下操作,它在增加优雅的同时又最大程度地减少了列表遍历:
partitionable( List ) :-
sum_prefix( List , Sum , Sfx ) ,
sum_prefix( Sfx , Sum , [] ) .
sum_prefix( List , Sum , Suffix ) :- sum_prefix(List,0,Sum,Suffix) .
sum_prefix( Suffix , Sum , Sum , Suffix ) .
sum_prefix( [H|List] , Acc , Sum , Suffix ) :-
Acc1 is Acc+H ,
sum_prefix(List,Acc1,Sum,Suffix)
.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.