【问题标题】:How to find the number of inversions in a list in Prolog如何在 Prolog 中的列表中查找反转数
【发布时间】:2015-04-16 18:12:11
【问题描述】:

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

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

据我了解,从 0...n 开始的数字矩阵中的求逆次数是小于被比较数字的元素总数(如果我在这方面有误,请纠正我)。

我对@9​​87654322@ 在 Prolog 中的工作方式有一点了解,但我很想知道一种更有效的方法来计算扁平矩阵中的反转数量。 Prolog 中的变量对我来说很奇怪,所以最好是简单的解释。

提前谢谢你!

【问题讨论】:

    标签: list prolog


    【解决方案1】:

    首先,我不太明白你所说的“倒置”的含义,所以我会坚持准规范的解释 @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.
      

    就是这样。让我们把它们放在一起!

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

    【讨论】:

    • 不错的解决方案!另请查看automaton/8:它可以让您将计数器与描述序列的自动机的状态转换相关联。在此示例中,序列将由 &gt;&lt;= 组成,映射到整数,例如 012,通过 CLP(FD) 约束与以下序列相关联这些关系适合对应位置的整数。
    • @mat。好主意! MyFirstAutomaton®(在 clpfd 中)。
    【解决方案2】:

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

    :- use_module(library(clpfd)).
    

    使用tfilter/3bool01_t/2sum/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 ,小姐)。 Ms0 = [1,1,-1,-1,1,0,1,1,1,1,-1,0,0,-1,1], 女士 = [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)。 女士 = [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]。

    或者,一次完成所有步骤:

    :- 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。

    【讨论】:

      【解决方案3】:

      一个可能的定义,尽量保持简单:

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

      direction/3 谓词比较一对元素,确定它们是否按升序/降序排列。此类信息在访问列表时向下传递,如果无法匹配,则递增计数器(累加器,从 0 开始)。当访问停止时(列表只有 1 个元素,则无法确定方向),累积的计数器被“向上传递”以在顶层调用中返回。

      我选择了 cut,而不是 'if/then/else' 构造,因此您可以尝试使用它自己重写 count_inversions/4(您可以看到它在 direction/3 中使用)。注意运算符优先级!

      注意:direction/3 忽略 A =:= B 时固有的歧义,并将“向上”分配给这种情况。

      HTH

      【讨论】:

      • 您能描述一下这些谓词中各种元素的作用吗?我理解空集基本情况,但谓词中字母背后的含义有点混乱。
      • s(X):狡猾,但对于包含相邻重复项的列表来说有些脆弱(取决于当前运行的方向)。例如,count_inversions([1,2,3,3,4,3],1) 成功,但 count_inversions([1,2,3,3,4,3,3],1) 失败。
      • count_inversions([8,2],N).count_inversions([8,2],N). 的结果都是N = 0
      • [8,2][2,8] 的反转次数为零。对于前一种情况应该是1。我说的对吗?
      • @OmG:你在 [8,2] 中看到了哪个 inversion? OP关于升序/降序优先阅读的问题中没有任何内容
      猜你喜欢
      • 2013-10-28
      • 2017-03-26
      • 1970-01-01
      • 2020-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多