检查列表之间的数字是否相等?

时间:2019-07-12 11:44:32

标签: prolog clpfd

一种有效的方法来检查序言中的列表之间有多少个相等的结束数字?

例如,我们有Lista = [4432,2345,3243]Listb = [3345,3232]

在这两个列表中,我们有 4432 3232 ,它们具有 2个相同的结束数字 3345、2345 ,其中 3个相同的结束数字 。 3243、3232具有相同的2个起始数字,但我不认为这是有效的。我以最慢的方式解决了这个问题,方法是将每个Lista的数量与每个Listb一起检查,但这非常很慢。如何更有效地解决此问题?


编辑1 :我对答案代码进行了一些更改,以设法找到相同的数字和剩余的子树,但是我无法将它们组合在一起以精确地找到一个列表具有相同的数字。你能帮我继续吗?

same_ending(Tree, N /*4904*/, N-Len-Last) :-
  atom_chars(N, Digs),
  reverse(Digs, RDigs),
  same_ending1(RDigs, Tree, [], Fin, 0, Lens),
  pow2(Lens, Res1),
  Res is Res1-1,
  Len = Res,
  Last = Fin.

same_ending1([], _, Curr, Fin, Len, Len).
same_ending1([Dig|Digs], Tree, Curr, Fin, Len, Len2) :-
  ( select(Dig-SubTree, Tree, _) ->
    ( succ(Len, Len1), append([Dig], Curr, Currn),
      same_ending1(Digs, SubTree, Currn, Fin, Len1, Len2) ) 
    ; 
    Len2 = Len,
    Fin = Curr-Tree
  ).

编辑2 :针对@gusbro答案的最终建议,我创建了此代码

ssame_endings(L1,[],Curr,Final):- Final=Curr.

ssame_endings(L1, L2,Curr,Final):-
  build_tree(L1, Tree),
  head(L2,H),
  tail(L2,T),  
  findall(Res,same_ending(Tree,H,Res) , Endings),
  append(Curr,Endings,Curr1),
  ssame_endings(L1,T,Curr1,Final).

head([A|_],A).
tail([_|A],A).

pow2(X,Z) :- Z is 2**X.

same_ending(Tree,N, N-Len/LItems):-
  atom_chars(N, Digs),
  reverse(Digs, RDigs),
  same_ending1(RDigs, Tree, 0, Len, SubTree),
  length(SDigs, Len),
  append(SDigs, _, RDigs),
  reverse(SDigs, RSDigs),
  same_ending2(SubTree, RSDigs, [], LItems).

same_ending1([], SubTree, Len, Len, SubTree1):-
  SubTree=[] -> SubTree1=[[]]; SubTree1=SubTree.
same_ending1([Dig|Digs], Tree, Len, Len2, SubTree):-
  (select(Dig-DigSubTree, Tree,Third) ->
    (succ(Len, Len1), same_ending1(Digs, DigSubTree, Len1, Len2, SubTree),same_ending1(Digs,Third,Len1,Len2,SubTree)) ;
    Len2-SubTree=Len-Tree
  ).

我按照建议使用了 findall ,以便结合给出的所有答案,我认为该部分是正确的,因为我已经写了它。然后我使用 select 之后,据我了解,如果我们有树[1- [2- [3,4]],并且我们希望同时具有1,2,3和1,2,4。如果我们有数字5321,则第三个参数也会给我们124。但是,我使用它的方式无法产生预期的结果。我在做什么错了?

2 个答案:

答案 0 :(得分:1)

您可以为第一个列表构建一个反转数字树,然后遍历第二个列表中每个反转项目的树。如果列表包含很多项目,这可能会更有效。

same_endings(L1, L2, Endings):-
  build_tree(L1, Tree),
  maplist(same_ending(Tree), L2, Endings).

same_ending(Tree, N, N-Len):-
  atom_chars(N, Digs),
  reverse(Digs, RDigs),
  same_ending1(RDigs, Tree, 0, Len).

same_ending1([], _, Len, Len).
same_ending1([Dig|Digs], Tree, Len, Len2):-
  (select(Dig-SubTree, Tree, _) ->
    (succ(Len, Len1), same_ending1(Digs, SubTree, Len1, Len2)) ;
    Len2=Len
  ).

build_tree(L, Tree):-
  foldl(add_tree, L, [], Tree).

add_tree(N, Tree, NTree):-
  atom_chars(N, Digs),
  reverse(Digs, RDigs),
  add_tree1(RDigs, Tree, NTree).

add_tree1([], Tree, Tree).
add_tree1([Dig|Digs], Tree, [Dig-SubTree1|Tree1]):-
  (select(Dig-SubTree, Tree, Tree1) -> true; SubTree-Tree1=[]-Tree),
  add_tree1(Digs, SubTree, SubTree1).

测试样本:

?- same_endings( [4432,2345,3243] , [3345,3232], Endings).
Endings = [3345-3, 3232-2].

您可以稍微修改一下此代码以获得具有相同结尾的实际项目。


对上面的代码稍作修改,您还可以列出第一个列表中的实际数字,这些数字在第二个列表中的每个项目上都有相同(最大)的结尾:

same_endings(L1, L2, Endings):-
  build_tree(L1, Tree),
  maplist(same_ending(Tree), L2, Endings).

same_ending(Tree, N, N-Len/LItems):-
  atom_chars(N, Digs),
  reverse(Digs, RDigs),
  same_ending1(RDigs, Tree, 0, Len, SubTree),
  length(SDigs, Len),
  append(SDigs, _, RDigs),
  reverse(SDigs, RSDigs),
  same_ending2(SubTree, RSDigs, [], LItems).

same_ending1([], SubTree, Len, Len, SubTree).
same_ending1([Dig|Digs], Tree, Len, Len2, SubTree):-
  (memberchk(Dig-DigSubTree, Tree) ->
    (succ(Len, Len1), same_ending1(Digs, DigSubTree, Len1, Len2, SubTree)) ;
    Len2-SubTree=Len-Tree
  ).

same_ending2([], _, LItems, LItems).
same_ending2([Dig-DigSubTree|SubTree], Digs, MItems, LItems):-
  (Dig=endmarker -> (number_chars(Item, Digs), NItems=[Item|MItems]) ;
    same_ending2(DigSubTree, [Dig|Digs], MItems, NItems)
  ), same_ending2(SubTree, Digs, NItems, LItems).

build_tree(L, Tree):-
  foldl(add_tree, L, [], Tree).

add_tree(N, Tree, NTree):-
  number_chars(N, Digs),
  reverse(Digs, RDigs),
  add_tree1(RDigs, Tree, NTree).

add_tree1([], Tree, [endmarker-[]|Tree]).
add_tree1([Dig|Digs], Tree, [Dig-SubTree1|Tree1]):-
  (select(Dig-SubTree, Tree, Tree1) -> true; SubTree-Tree1=[]-Tree),
  add_tree1(Digs, SubTree, SubTree1).

测试用例:

?- same_endings( [4432,2345,3243] , [3345,3232], Endings).
Endings = [3345-3/[2345], 3232-2/[4432]].
?- same_endings( [4432,2345,3243,2195345,2345] , [3345,3232,19232,2195345], Endings).
Endings = [3345-3/[2195345, 2345, 2345], 3232-2/[4432], 19232-2/[4432], 2195345-7/[2195345]].

对于某项不共享任何结尾数字的情况,没有特殊处理,在这种情况下,代码只会吐出整个列表。

答案 1 :(得分:1)

shared_tail_len_atom(A,B,N,S) :-
    sub_atom(A,_,N,0,S),
    N>=1, % raise this value to reduce overhead
    sub_atom(B,_,N,0,S).

same_endings_easy(As,Bs,Es) :-
    setof(N-ab(A,B),
          aggregate(
              max(C),
              S^( member(A,As),
                  member(B,Bs),
                  shared_tail_len_atom(A,B,C,S)
              ), N), Es).

您可以尝试这个简单的代码段。 sub_atom / 5是内置的ISO Prolog,但是它可以与数字一起使用的事实可能是SWI-Prolog扩展。

?- same_endings_easy([123,223,333],[423,433],Endings).
Endings = [1-ab(123, 433), 1-ab(223, 433), 1-ab(333, 423), 2-ab(123, 423), 2-ab(223, 423), 2-ab(333, 433)].