如何在Prolog中查找列表中的反转次数

时间:2015-04-16 18:12:12

标签: list prolog

作为Prolog的新手,我正在寻找一种计算列表中反转次数的好方法。

我知道如何使用flatten(Matrix, FlatMatrix)展平矩阵,从而创建一个包含矩阵中单个元素集的变量。但是,我不确定如何查找该列表中的反转次数。

根据我的理解,从0 ... n开始的数字矩阵中的反转次数是小于被比较数字的元素总数(如果我错了,请纠正我)

我对Prolog中的setof/3如何运作有一点了解,但我很想知道一种更有效的方法来解决平坦矩阵中的反转次数。 Prolog中的变量对我来说很奇怪,所以简单的解释是最好的。

提前谢谢!

3 个答案:

答案 0 :(得分:2)

首先,我并没有完全理解你所谓的“反转”,所以我会坚持准规范的解释 @CapelliC使用in his answer来解决这个问题。

我们假设所有列表项都是整数,因此我们可以使用

:- use_module(library(clpfd)).

z_z_order(X,Y,Op) :-
   zcompare(Op,X,Y).

要计算反转次数(上下方向变化),我们执行以下四个步骤:

  1. 比较相邻的项目(使用mapadj/3,如本答案最后所定义的那样)

    ?- 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. 消除=Cs0的所有出现(使用 tfilter/3dif/3

    ?- Cs0 = [<,<,>,>,<,=,<,<,<,<,>,=,=,>,<,<], tfilter(dif(=),Cs0,Cs1).
    Cs0 = [<,<,>,>,<,=,<,<,<,<,>,=,=,>,<,<],
    Cs1 = [<,<,>,>,<,  <,<,<,<,>,    >,<,<].
    
  3. Cs1中运行相同的项目(使用 splitlistIfAdj/3dif/3

    ?- Cs1 = [<,<,>,>,<,<,<,<,<,>,>,<,<], splitlistIfAdj(dif,Cs1,Cs).
    Cs1 = [ <,< , >,> , <,<,<,<,< , >,> , <,< ],
    Cs  = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]].
    
  4. 反转次数比运行次数少一次(使用 length/2(#=)/2

    ?- Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]], length(Cs,L), N #= max(0,L-1).
    Cs = [[<,<],[>,>],[<,<,<,<,<],[>,>],[<,<]], L = 5, N = 4.
    
  5. 就是这样。让我们把它们放在一起!

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

    样本使用:

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

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

答案 1 :(得分:1)

这是my previous answer的替代方案。它基于 mapadj/3

:- use_module(library(clpfd)).

使用 tfilter/3bool01_t/2 sum/3我们定义:

z_z_momsign(Z0,Z1,X) :-
   X #= max(-1,min(1,Z1-Z0)).

z_z_absmomsign(Z0,Z1,X) :-
   X #= min(1,abs(Z1-Z0)).

#\=(X,Y,Truth) :-
   X #\= Y #<==> B,
   bool01_t(B,Truth).

最后,我们像这样定义zs_invcount/2

zs_invcount(Zs,N) :-
   mapadj(z_z_momsign,Zs,Ms0),
   tfilter(#\=(0),Ms0,Ms),
   mapadj(z_z_absmomsign,Ms,Ds),
   sum(Ds,#=,N).

样品使用:

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

修改

更详细地考虑执行以下示例查询:

?- zs_invcount([1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8],N).

让我们一步一步地进行!

  1. 对于所有相邻的列表项,计算其“动量”的符号:

    ?- Zs = [1,2,4,3,2,3,3,4,5,6,7,6,6,6,5,8], mapadj(z_z_momsign,Zs,Ms0).
    Zs  = [1,2, 4,3, 2,3,3,4,5,6,7, 6,6,6, 5,8],
    Ms0 = [ 1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1 ].
    
  2. 消除0的所有符号值:

    ?- Ms0 = [1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1], tfilter(#\=(0),Ms0,Ms).
    Ms0 = [1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1],
    Ms  = [1,1,-1,-1,1,  1,1,1,1,-1,    -1,1].
    
  3. 获得“动量反转”,即动量冲动的绝对迹象。

    ?- Ms = [1,1,-1,-1,1,1,1,1,1,-1,-1,1], mapadj(z_z_absmomsign,Ms,Ds).
    Ms = [1,1,-1,-1,1,1,1,1,1,-1,-1,1],
    Ds = [ 0,1, 0, 1,0,0,0,0,1, 0, 1 ].
    
  4. 最后,使用sum/3总结“动量反转”的数量:

    ?- Ds = [0,1,0,1,0,0,0,0,1,0,1], sum(Ds,#=,N).
    N = 4, Ds = [0,1,0,1,0,0,0,0,1,0,1].
    
  5. 或者,或者,所有步骤:

    :- Zs  = [1,2,4, 3, 2,3,3,4,5,6,7, 6,6,6, 5,8], mapadj(z_z_momsign,Zs,Ms0),
       Ms0 = [ 1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1 ], tfilter(#\=(0),Ms0,Ms),
       Ms  = [ 1,1,-1,-1,1,  1,1,1,1,-1,    -1,1 ], mapadj(z_z_absmomsign,Ms,Ds),
       Ds  = [  0,1, 0, 1, 0, 0,0,0,1,   0,   1  ], sum(Ds,#=,N),
       N   = 4.
    

答案 2 :(得分:0)

一个可能的定义,试图让它尽可能简单:

count_inversions(L, N) :-
    direction(L, D, L1),
    count_inversions(L1, D, 0, N).

direction([A,B|L], D, [B|L]) :-
    A > B -> D = down ; D = up.

count_inversions([_], _, N, N).
count_inversions(L, D, M, N) :-
    direction(L, D, L1),
    !, count_inversions(L1, D, M, N).
count_inversions(L, _, M, N) :-
    direction(L, D1, L1),
    M1 is M+1, count_inversions(L1, D1, M1, N).

方向/ 3谓词比较一对元素,确定它们是否按升序/降序排列。这些信息传递下来访问列表,如果它不能匹配,则计数器递增(累加器,从0开始)。当访问停止时(列表只有1个元素,则无法确定方向),累积的计数器将被传递出去&#39;在顶级电话会议上退回。

我选择了剪辑,而不是&#39; if / then / else&#39;构造,所以你可以尝试使用它自己重写count_inversions / 4(你可以看到它在方向/ 3中使用)。谨防运营商的优先权!

注意:当A =:= B时,方向/ 3忽略了固有的模糊性,并且指定了&#39; up&#39;对于这种情况。

HTH