【问题标题】:How to access list permutations in prolog?如何访问序言中的列表排列?
【发布时间】:2012-02-03 19:29:09
【问题描述】:

我想访问列表排列并将其作为参数传递给其他函数。

这是排列代码:

takeout(X,[X|R],R).  
takeout(X,[F|R],[F|S]) :-
   takeout(X,R,S),
   write(S).

perm([X|Y],Z) :-
   perm(Y,W),
   takeout(X,Z,W).  
perm([],[]).

【问题讨论】:

  • 请更清楚地解释您的问题。你在运行什么不起作用?您想看到什么,但没有看到,或看到您不想看到的?
  • 我想从用户那里得到一个数字列表,并显示所有最大堆树,所以我需要列表的排列并将其作为参数发送给最大堆函数,(对不起我英语不好)

标签: list prolog permutation prolog-dif


【解决方案1】:

首先,让我们重新定义您的谓词,这样它们就不会执行任何不必要的 I/O:

takeout(X,[X|R],R).  
takeout(X,[F |R],[F|S]) :- takeout(X,R,S).

perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).  
perm([],[]).

现在您有了可以被视为“纯”置换函数的东西:

?- perm([1,2,3], X).
X = [1, 2, 3] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [1, 3, 2] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.

因此,假设您有一个 max_heap 函数,它接受一个值列表并生成一棵树。我会让你担心这个,所以让我们假设它存在并且被称为max_heap/2,让我们进一步假设你有一种方法来显示这个有吸引力的称为display_heap/1。要“获取”排列并将其作为参数“发送”给这些函数,您实际上是在用数学语言说:假设 P 是 X 的排列,让我们用它创建一个 max_heap 并显示它。或者,假设 P 是 X 的排列,H 是由 X 组成的最大堆,让我们显示 H:

show_heaps(List) :- perm(List, P), max_heap(P, H), display_heap(H).

这和我的英文句子说的一样:假设 P 是列表的排列,那么 H 是它的堆表示,然后显示它。从技术上讲,display_heap/1 仍然是一个谓词,对于给定的堆可能是真或假。在实践中,它总是正确的,如果你运行它,你仍然需要反复点击; 说,给我另一个解决方案,除非你使用失败驱动的循环或像findall/3 这样的超逻辑谓词来找到所有解决方案。

编辑:让我们讨论失败驱动的循环和findall/3。首先让我添加一些新的谓词,因为我不知道你在做什么,但这对我们的目的来说并不重要。

double([X|Xs], [Y|Ys]) :- Y is X*2, double(Xs, Ys).
double([],[]).

showlist(Xs) :- print(Xs).

所以现在我有一个谓词double/2 将列表中的值加倍,还有一个谓词showlist/1 在标准输出上打印列表。我们可以这样试一试:

?- perm([1,2,3], X), double(X, Y), showlist(Y).
[2,4,6]
X = [1, 2, 3],
Y = [2, 4, 6] ;
[4,2,6]
X = [2, 1, 3],
Y = [4, 2, 6] ;
[4,6,2]
X = [2, 3, 1],
Y = [4, 6, 2] ;
[2,6,4]
X = [1, 3, 2],
Y = [2, 6, 4] ;
[6,2,4]
X = [3, 1, 2],
Y = [6, 2, 4] ;
[6,4,2]
X = [3, 2, 1],
Y = [6, 4, 2] ;
false.

当您输入; 时,您是在说“或者?”到序言。换句话说,您是在说“还有什么?”你告诉Prolog,实际上,这不是我想要的答案,试着给我找另一个我更喜欢的答案。你可以用一个失败驱动的循环来形式化这个过程:

?- perm([1,2,3], X), double(X, Y), showlist(Y), fail.
[2,4,6][4,2,6][4,6,2][2,6,4][6,2,4][6,4,2]
false.

所以现在你看到每个排列的输出都经过double/2,然后 Prolog 报告错误。这就是这样的意思:

show_all_heaps(List) :- perm(List, X), double(X, Y), showlist(Y), nl, fail.
show_all_heaps(_).

看看它是如何工作的:

?- show_all_heaps([1,2,3]).
[2,4,6]
[4,2,6]
[4,6,2]
[2,6,4]
[6,2,4]
[6,4,2]
true.

另一个选项是使用findall/3,看起来更像这样:

?- findall(Y, (perm([1,2,3], X), double(X, Y)), Ys).
Ys = [[2, 4, 6], [4, 2, 6], [4, 6, 2], [2, 6, 4], [6, 2, 4], [6, 4, 2]].

使用它来解决你的问题可能超出了你正在做的任何家庭作业的范围。

【讨论】:

  • 我不明白你的意思“除非你使用失败驱动的循环或像 findall/3 这样的超逻辑谓词来找到所有解决方案。”???????
  • 我在回答中添加了一些解释。
【解决方案2】:

我们可以像这样根据same_length/2select/3定义list_permutation/2

:- use_module(library(lists),[same_length/2,select/3]).

list_permutation(As,Bs) :-
   same_length(As,Bs),          % redundant goal helps termination
   list_permutation_(As,Bs).

list_permutation_([],[]).
list_permutation_([A|As],Bs0) :-
    select(A,Bs0,Bs),
    list_permutation_(As,Bs).

感谢same_length/2,以下两个查询1,2 普遍终止:

?- list_permutation([1,2,3],Ys).
  Ys = [1,2,3]
; Ys = [1,3,2]
; Ys = [2,1,3]
; Ys = [3,1,2]
; Ys = [2,3,1]
; Ys = [3,2,1]
; false.

?- list_permutation(Xs,[1,2,3]).
  Xs = [1,2,3]
; Xs = [1,3,2]
; Xs = [2,1,3]
; Xs = [2,3,1]
; Xs = [3,1,2]
; Xs = [3,2,1]
; false.

到目前为止,一切都很好。但是如果有重复的列表项,答案序列是什么样的呢?

?- list_permutation([1,1,1],Ys).
  Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; false.

5/6 个答案是多余的!我们可以做什么?我们只是使用select<b>d</b>/3而不是select/3

list_permuted(As,Bs) :- 相同长度(As,Bs), list_permuted_(As,Bs)。 list_permuted_([],[])。 list_permuted_([A|As],Bs0) :- 选择(A,Bs0,Bs),%使用选择/ 3,不选择/ 3 list_permuted_(As,Bs)。

让我们重新运行上面给出了 5 个冗余解决方案的查询!

?- list_permuted([1,1,1],Ys).
  Ys = [1,1,1]
; false.

?- list_permuted(Xs,[1,1,1]).
  Xs = [1,1,1]
; false.

更好!所有多余的答案都消失了。

让我们比较一些示例案例的解决方案集:

?- _Xs = [1,2,1,1,2,1,1,2,1], setof(Ys,list_permutation(_Xs,Ys),Yss), setof(Ys,list_permuted(_Xs,Ys),Yss), 长度(Yss,N)。 N = 84, Yss = [[1,1,1,1,1,1,2,2,2],[1,1,1,1,1,2,1,2,2],[.. .|...]|...]。

好的!对于规模稍大的问题,经验运行时测量如何?
我们使用call_time/2 以毫秒为单位测量运行时间T_ms

?- call_time(\+ (list_permutation([1,2,1,1,1,2,1,1,1,2,1],_),false),T_ms).
T_ms = 8110.

?- call_time(\+ (list_permuted(   [1,2,1,1,1,2,1,1,1,2,1],_),false),T_ms).
T_ms = 140.

好的!正确编译if_/3(=)/3list_permuted/2 会更快!


脚注 1:使用 SICStus Prolog 版本 4.3.2 (x86_64-linux-glibc2.12)。
脚注 2:Prolog 顶层给出的答案已发布为了可读性而处理。

【讨论】:

    【解决方案3】:

    如果您只是想探索最后没有“False”的排列,这段代码可能会有所帮助

    takeout(X,[F |R],[F|S]) :- F\=X, takeout(X,R,S).
    takeout(X,[X|R],R).
    perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).  
    perm([],[]).
    

    所以 perm([a,b],B) 的输出是

    B=[a,b]
    B=[b,a]
    

    【讨论】:

      猜你喜欢
      • 2022-10-18
      • 1970-01-01
      • 2014-05-05
      • 2022-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多