问题如下:在Prolog most_common_sublist(L1,N,L2(中编写一个谓词,它将找到长度为N的子列表L2,使其成为L1中最常见的子列表。
//Example 1:
?- most_common_sublist([1,2,2,3,2,2,4,2,2,3],1,L).
L=[2];
//Example 2:
?- most_common_sublist([1,2,2,3,2,2,4,2,2,3],2,L).
L=[2,2];
//Example 3:
?- most_common_sublist([1,2,2,3,2,2,4,2,2,3],3,L).
L=[2,2,3];
我的方法是使用生成器谓词生成大小为N的所有可能的子列表,使用检查谓词检查其中哪一个是列表中最常见的,然后将其作为我的结果。我之所以不使用内置的长度和加法谓词,是因为我应该自己编写。我的生成器谓词有效,它给出了正确的输出。
?- generator([1,2,2,3,2,2,4,2,2,3],3,L).
L = [[1, 2, 2], [2, 2, 3], [2, 3, 2], [3, 2, 2], [2, 2, 4], [2, 4, 2], [4, 2|...], [2|...]] [write]
L = [[1, 2, 2], [2, 2, 3], [2, 3, 2], [3, 2, 2], [2, 2, 4], [2, 4, 2], [4, 2, 2], [2, 2, 3]]
我检查了我所有的谓词,它们似乎都有效(至少对于我正在使用的测试用例(,问题出现在检查谓词上。它似乎工作得很好,直到它达到N>P(当这不是真的时,当它是真的时工作正常(。我希望程序转到它下面的下一个检查谓词(第三个检查谓词(,以便在Result中存储Temp值,而不是H值。出于某种原因,它没有进入第三个检查谓词(我用调试器检查过(,而是做了一些奇怪的事情(我不知道是什么(。
most_common_sublist(L,N,Result):-generator(L,N,LOP),check(LOP,_,Temp),add(Temp,[],Result).
add([],L,L).
add([X|L1],L2,[X|L3]):-add(L1,L2,L3).
length([],0).
length([X|O],N):-length(O,M),N is M+1.
sublist([H|_],1,[H]).
sublist([H|T],N,[H|LOP]):-M is N-1,sublist(T,M,LOP).
generator(L,N,[L]):-length(L,M),N=:=M.
generator([H|T],N,LOP):-sublist([H|T],N,PN),generator(T,N,LP),add([PN],LP,LOP).
check([],Z,K):-Z is 0,add([],[],K).
check([H|T],Hits,Result):-check_how_many(H,[H|T],N),check(T,P,_),N>=P,Hits is N,add(H,[],Result).
check([H|T],Hits,Result):-check_how_many(H,[H|T],N),check(T,P,Temp),Hits is P,add(Temp,[],Result).
check_how_many(X,[X],1).
check_how_many(_,[_],0).
check_how_many(Pattern,[H|T],Hits):-same(Pattern,H),check_how_many(Pattern,T,P),Hits is P+1.
check_how_many(Pattern,[_|T],Hits):-check_how_many(Pattern,T,P),Hits is P.
same([], []).
same([H1|R1], [H2|R2]):-
H1 = H2,
same(R1, R2).
由于我不熟悉您的代码,我用类似的功能重写了它。%here
后面的行是我的改进(使用了2次(。为了简单起见,我使用了内置谓词length/2
和append/3
,而不是add/3
。sublist/3
具有完全不同的代码但相同的功能,same/2
根本不是必需的。add/3
的大多数使用都是不必要的,还有一些平等声明。
most_common_sublist(L,N,Temp):-
generator(L,N,LOP),
check(LOP,_,Temp).
sublist(L,N,S):-
length(S,N),
append(S,_,L).
generator(L,N,[L]):-
length(L,N).
generator([H|T],N,LOP):-
sublist([H|T],N,PN),
generator(T,N,LP),
append([PN],LP,LOP).
check([],0,[]).
check([H|T],N,H):-
check_how_many(H,[H|T],N),
check(T,P,_),
N>=P.
check([H|T],P,Temp):-
check_how_many(H,[H|T],N),
check(T,P,Temp)
%here
, N=<P
.
check_how_many(X,[X],1).
check_how_many(_,[_],0).
check_how_many(H,[H|T],Hits):-
check_how_many(H,T,P),
Hits is P+1.
check_how_many(Pattern,[H|T],P):-
%here
Pattern == H,
check_how_many(Pattern,T,P).
在放弃跟踪后,我只使用以下调用在启用长输出后进行调试(?- set_prolog_flag(answer_write_options,[max_depth(100)]).
):
?- findall(Temp,check([[1, 2, 2], [2, 2, 1]],_,Temp),Out).
初始输出为
Out = [[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[2,2,1],[2,2,1],[],[],[2,2,1],[2,2,1],[],[]].
它包含了很多空列表。第一个修复(%here
(是为最后一个check/3
情况设置条件N=<P
。到目前为止,可以选择低于N
的P
,这应该被第二种check/3
情况所涵盖。输出更改为
Out = [[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[2,2,1],[2,2,1],[2,2,1],[]].
更好,但仍然可以空列表。类似的情况发生在上一个check_how_many/3
的情况中:您必须声明H
和Pattern
不同,否则可能不计算拟合的Pattern
。让我们检查输出
Out = [[1,2,2],[1,2,2],[1,2,2],[2,2,1]].
好多了。让我们检查另一种情况:
?- findall(Temp,check([[1, 2, 2], [1, 2, 2], [2, 2, 1]],_,Temp),Out).
Out = [[1,2,2],[1,2,2],[1,2,2],[1,2,2]].
?- findall(Temp,check([[1, 2, 2], [2, 2, 2], [1, 2, 2]],_,Temp),Out).
Out = [[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[2,2,2],[2,2,2],[2,2,2],[1,2,2]].
工作。。。几乎
所以问题似乎是check_how_many/3
:改变
check_how_many(_,[_],0).
至
check_how_many(_,[],0).
你应该没事的。
?- findall(Temp,check([[1, 2, 2], [2, 2, 2], [1, 2, 2]],_,Temp),Out).
Out = [[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2],[1,2,2]].
由于自己编写代码比调试外来代码有趣得多,我将在尝试中添加另一个答案。
自己编写代码比调试外来代码有趣得多。这是我的尝试。它的工作原理与您的不同,因为我不计算可能的子集;剩余的";列表我使用内置谓词length/2
、append/3
和member/2
,它们各有3行要写。
% check how often 2.nd attribute List occurs in 1st attribute List.
countit([],_,Val,Val).
countit([H|In],Out,Past,Future):-
( append(Out,_,[H|In])
-> Present is Past+1,
countit(In,Out,Present,Future)
; countit(In,Out,Past,Future)
).
mostCommonSublist(In,N,Out):-
maxStartList(In,N,OutList,Max),
member((Max,Out),OutList).
% for every endlist calculate how often the first N elements appear within the endlist, track the max
maxStartList(In,N,[(1,In)],1):-
length(In,N),
!.
maxStartList([H|In],N,[(CntH,Curr)|MaxList],Max):-
length(Curr,N),
countit([H|In],Curr,0,CntH),
maxStartList(In,N,MaxList,CntIn),
Max is max(CntH , CntIn).
主谓词mostCommonSublist/3
调用谓词maxStartList/4
来获取所有子列表/计数对。然后,它验证子列表的计数是否等于最大值。这对于检查具有相同(最大(计数的不同答案是必要的。
maxStartList/4
从输入列表中删除元素,并计算当前列表的开始频率。它还跟踪最大值。
对于当前输入列表,调用计算谓词countit/4
。它为给定的inputlist(第一个参数(计算子列表(第二个参数(的出现次数
我的代码实际上使用了一个扭曲:第一次调用countit/4
时,子列表的内容没有统一,只是设置了子列表的长度。在第一次递归中,它会将所有条目与inputlist中的起始元素统一起来,并对其进行计数。在接下来的递归步骤中,如果子列表是完全已知的。使用if-then-else(..->..;..)
剩余输入列表的两种情况是否以子列表开头,谓词基本上计算出现次数。直到剩余的输入列表只剩下N
个元素(length(In,N)
(。
计算出的计数/子列表对存储在列表中,并跟踪最大值。
在知道所有计数/子列表对之后,我通过声明接受的子列表的计数必须等于最大值来最终确定这一切。
好的一面是,没有不准确的答案。
?- mostCommonSublist([1,2,2,3,2,2,4,2,2,3],3,L).
L = [2,2,3] ;
false.
?- mostCommonSublist([1,2,2,1,2,1,2,2,2,3],3,L).
L = [1,2,2] ;
L = [2,1,2] ;
false.
?- mostCommonSublist([1,2,2,1,2,1,2,2,2,1],2,L).
L = [1,2] ;
L = [2,2] ;
L = [2,1] ;
false.