【问题标题】:permutation of list with multiple same elements Prolog具有多个相同元素的列表的排列Prolog
【发布时间】:2011-04-08 16:19:55
【问题描述】:

大家好,请原谅任何对语言的滥用

我需要创建 myPermutation(L1,L2)。给定一个列表 L1(它具有许多连续出现的元素)返回一个列表 L2,该列表 L1 以没有两个相同的连续元素的方式排序)

例子:给定列表 L1[1,1,1,1,1,2,2,3,3,4,4,5,5] L2 应该是 [1,2,1,5,1, 3,1,4,1,2,3,5,4] 我已经尝试过随机排列并检查每个排列的一致性,但它非常慢(L1 大约 24 cpu,超过 12 个元素)

唯一可能的解决方案是进行一致的排列而不是检查一个 但是我该怎么做呢?

即使使用标准 prolog 也可以做到这一点,但由于我对逻辑编程的理解很差,我无法理解它

谢谢你:D

【问题讨论】:

  • 是否应该以不“重复”解决方案的方式完成排列,这些解决方案涉及相同的项目出现在与另一个解决方案相同的位置?这是我首先想到要担心的事情。在解决方案的序列中避免“重复”似乎是对通常的“置换”谓词的相当简单的修改。

标签: list prolog permutation prolog-dif


【解决方案1】:

您可以构建这样的排列来检查列表。

myPermutation([], []).
myPermutation(L, [H|P]):-
  select(H, L, NL), % Select one item from the list
  myPermutation(NL, H, P).

myPermutation([], _, []).
myPermutation(L, H, [I|P]):-
  select(I, L, NL), % Select another item
  I \= H, % Enforce restriction of no duplicate consecutive items
  myPermutation(NL, I, P).

此代码将在回溯时给出所有有效的排列。我将留给你一个练习,作为一种丢弃重复排列的方法。

【讨论】:

  • 我误读了 OP 的要求。 +1 给你,我发布了一个基于 dif/2 的新替代方案。
【解决方案2】:

您可以使用dif/2 快速完成此操作,它可以在事先不知道这些值的情况下限制两个变量具有不同的值:

?- dif(X,Y).
dif(X, Y).

?- dif(X,Y), X=1.
X = 1,
dif(1, Y).

?- dif(X,Y), X=1, Y=1.
false.

使用它,您可以创建一个谓词来约束一个列表,使得没有两个元素是相同的:

conseq_dif([]).
conseq_dif([_]).
conseq_dif([X,Y|Xs]) :-
    dif(X,Y),
    conseq_dif([Y|Xs]).

现在找到你想要的约束排列:

constrained_perm(Lst,Prm) :-
    length(Lst,N),
    length(Prm,N),           % make list of N unbound variables
    conseq_dif(Prm),
    permutation(Lst,Prm).    % "ordinary" (library) permutation finding

我不确定dif/2 是否是标准 Prolog,但主要实现都有它。

【讨论】:

    【解决方案3】:

    我们根据same_length/2定义my_perm/2list_permuted/2mapadj/2

    my_perm(Xs,Ys) :-
       same_length(Xs,Ys),
       mapadj(dif,Ys),
       list_permuted(Xs,Ys).
    

    通用的mapadj/2可以这样定义:

    :- meta_predicate mapadj(2,?), list_mapadj(?,2), list_prev_mapadj(?,?,2).
    mapadj(P_2,Xs) :-
       list_mapadj(Xs,P_2).
    
    list_mapadj([],_).
    list_mapadj([A|As],P_2) :-
       list_prev_mapadj(As,A,P_2).
    
    list_prev_mapadj([],_,_).
    list_prev_mapadj([A1|As],A0,P_2) :-
       call(P_2,A0,A1),
       list_prev_mapadj(As,A1,P_2).
    

    OP给出的示例查询1,2来了。

    我们使用call_time/2 以毫秒为单位测量运行时间T_ms

    ?- call_time(my_perm([1,1,1,1,1,2,2,3,3,4,4,5,5],[1,2,1,5,1,3,1,4,1,2,3,5,4]),T_ms).
    T_ms = 0.
    

    我们需要多长时间才能找到前几个解决方案?

    ?- call_time(my_perm([1,2,1,5,1,3,1,4,1,2,3,5,4],Xs),T_ms).
      T_ms =  0, Xs = [1,2,1,5,1,3,1,4,1,2,3,5,4]
    ; T_ms =  0, Xs = [1,2,1,5,1,3,1,4,1,2,3,4,5]
    ; T_ms = 10, Xs = [1,2,1,5,1,3,1,4,1,2,5,3,4]
    ; T_ms = 10, Xs = [1,2,1,5,1,3,1,4,1,2,4,3,5]
    ; T_ms = 10, Xs = [1,2,1,5,1,3,1,4,1,2,5,4,3]
    ; T_ms = 10, Xs = [1,2,1,5,1,3,1,4,1,2,4,5,3]
    ; T_ms = 10, Xs = [1,2,1,5,1,3,1,4,1,3,2,5,4]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,3,2,4,5]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,5,2,3,4]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,4,2,3,5]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,5,2,4,3]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,4,2,5,3]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,3,5,2,4]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,3,4,2,5]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,5,3,2,4]
    ; T_ms = 20, Xs = [1,2,1,5,1,3,1,4,1,4,3,2,5]
    ; T_ms = 30, Xs = [1,2,1,5,1,3,1,4,1,5,4,2,3]
    ; T_ms = 30, Xs = [1,2,1,5,1,3,1,4,1,4,5,2,3]
    ...
    

    请注意,T_ms 是单调增长的:它测量自首次调用给定目标以来所花费的时间。

    列举所有解决方案需要多长时间?

    ?- call_time(\+((my_perm([1,1,1,1,1,2,2,3,3,4,4,5,5],_),false)),T_ms).
    T_ms = 4030.
    

    有多少种解决方案?

    ?- use_module(library(aggregate)),
       aggregate(count,Xs,my_perm([1,2,1,5,1,3,1,4,1,2,3,5,4],Xs),N).
    N = 197664.
    

    脚注 1:使用 SICStus Prolog 版本 4.3.2 (x86_64-linux-glibc2.12)。
    脚注 2:为了便于阅读,对 Prolog 处理器给出的答案序列进行了调整。

    【讨论】:

    • goal_rt/2 不嵌套。请这样做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-16
    • 1970-01-01
    相关资源
    最近更新 更多