How to find the number of inversions in a list in Prolog

前端 未结 3 1198
一整个雨季
一整个雨季 2021-01-22 22:50

As someone who\'s new to Prolog, I\'m looking to find out what a good way to count the number of inversions in a list.

I know how to flatten a matrix using flatten

3条回答
  •  南笙
    南笙 (楼主)
    2021-01-22 23:10

    First, I didn't quite get the meaning of what you were calling "inversion", so I'll stick to the quasi-canonical interpretation that @CapelliC used in his answer to this question.

    Let's assume that all list items are integers, so we can use clpfd.

    :- use_module(library(clpfd)).
    
    z_z_order(X,Y,Op) :-
       zcompare(Op,X,Y).
    

    To count the number of inversions (up-down direction changes), we do the following four steps:

    1. compare adjacent items (using mapadj/3, as defined at the very end of this answer)

      ?- Zs = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8], mapadj(z_z_order,Zs,Cs0).
      Zs  = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8],
      Cs0 = [ <,<,>,>,<,=,<,<,<,<,>,=,=,>,< ].
      
    2. eliminate all occurrences of = in Cs0 (using tfilter/3 and dif/3)

      ?- Cs0 = [<,<,>,>,<,=,<,<,<,<,>,=,=,>,<,<], tfilter(dif(=),Cs0,Cs1).
      Cs0 = [<,<,>,>,<,=,<,<,<,<,>,=,=,>,<,<],
      Cs1 = [<,<,>,>,<,  <,<,<,<,>,    >,<,<].
      
    3. get runs of equal items in Cs1 (using splitlistIfAdj/3 and dif/3)

      ?- Cs1 = [<,<,>,>,<,<,<,<,<,>,>,<,<], splitlistIfAdj(dif,Cs1,Cs).
      Cs1 = [ <,< , >,> , <,<,<,<,< , >,> , <,< ],
      Cs  = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]].
      
    4. the number of inversions is one less than the number of runs (using length/2 and (#=)/2)

      ?- Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]], length(Cs,L), N #= max(0,L-1).
      Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]], L = 5, N = 4.
      

    That's it. Let's put it all together!

    zs_invcount(Zs,N) :-
       mapadj(z_z_order,Zs,Cs0),
       tfilter(dif(=),Cs0,Cs1),
       splitlistIfAdj(dif,Cs1,Cs),
       length(Cs,L),
       N #= max(0,L-1).
    

    Sample uses:

    ?- zs_invcount([1,2,3],0),    
       zs_invcount([1,2,3,2],1),    
       zs_invcount([1,2,3,3,2],1),               % works with duplicate items, too
       zs_invcount([1,2,3,3,2,1,1,1],1),
       zs_invcount([1,2,3,3,2,1,1,1,4,6],2),
       zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1],3),
       zs_invcount([1,2,3,3,2,1,1,1,4,6,9,1,1],3).
    true.
    

    Implementation of meta-predicate mapadj/3

    :- meta_predicate mapadj(3,?,?), list_prev_mapadj_list(?,?,3,?).
    mapadj(P_3,[A|As],Bs) :-
       list_prev_mapadj_list(As,A,P_3,Bs).
    
    list_prev_mapadj_list([]     ,_ , _ ,[]).
    list_prev_mapadj_list([A1|As],A0,P_3,[B|Bs]) :-
       call(P_3,A0,A1,B),
       list_prev_mapadj_list(As,A1,P_3,Bs).
    

提交回复
热议问题