Checking equal digits of numbers between lists?

烂漫一生 提交于 2019-12-13 10:25:41

问题


What would be an efficient way to check how many equal ending digits have numbers between lists in prolog?

For example we have Lista = [4432,2345,3243] and Listb = [3345,3232].

In these two lists we have 4432 and 3232 which have 2 same ending digits and 3345 , 2345 which have 3 same ending digits. 3243 , 3232 have the same 2 starting digits but I don't count this as valid. I have solved this problem in the slowest way , by checking each number of Lista with each number of Listb, but this is very slow. How can I solve this problem more efficiently?


Edit 1: I applied some changes to the answer code I managed to find the same digits and the subtree remaining , however I cannot combine them in order to find exactly the numbers that the element of the one list has the same digits with them. Can you help me proceed?

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
  ).

Edit 2: For the final suggestion on @gusbro 's answer I created this code

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
  ).

I used findall as suggested in order to combine all the answers given and I think that part is correct as I have written it. Then I used select after that which as I understand if we have the tree [1-[2-[3,4]] and we want both 1,2,3 and 1,2,4. if we have the number 5321 the third parameter will also give us 124. However the way I am using it does not produce the expected outcome. What I am doing wrong?


回答1:


You can build a tree of reversed digits for the first list, then traverse the tree for each reversed item in the second list. This may be more efficient if the list have many items.

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).

Test sample:

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

You may modify this code a bit to obtain the actual items which have the same endings.


With a slight modification of the code above you can also list the actual numbers from the first list that have the same (maximum) ending on each item from the second list:

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).

Test cases:

?- 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]].

There's no special treatment for the case when an item does not share any ending digit, the code just spits the whole list in that case.


If you want to get all the items with at least 1 matching ending digit, just change the procedures same_endings and same_endings1 with these ones:

same_endings(L1, L2, Endings):-
  build_tree(L1, Tree),
  findall(Ending, (member(N, L2), same_ending(Tree, N, Ending)), Endings).

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



回答2:


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).

You can try this simple snippet. sub_atom/5 it's an ISO Prolog builtin, but the fact that it works with numbers could be a SWI-Prolog extension.

?- 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)].


来源:https://stackoverflow.com/questions/57006348/checking-equal-digits-of-numbers-between-lists

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!