使prolog谓词具有确定性

使prolog谓词具有确定性,prolog,Prolog,我编写了一个谓词,shuffle/3,它生成两个列表的“shuffle”。当第二个和第三个参数被实例化时,第一个参数将成为一个列表,其中包含左和右的所有元素,它们的显示顺序与左和右相同 例如: ?- shuffle(X, [1, 2], [3, 4]). X = [1, 3, 2, 4] ; X = [1, 3, 4, 2] ; X = [1, 2, 3, 4] ; X = [3, 4, 1, 2] ; X = [3, 1, 2, 4] ; X = [3, 1, 4, 2] ; false.

我编写了一个谓词,
shuffle/3
,它生成两个列表的“shuffle”。当第二个和第三个参数被实例化时,第一个参数将成为一个列表,其中包含左和右的所有元素,它们的显示顺序与左和右相同

例如:

?- shuffle(X, [1, 2], [3, 4]).
X = [1, 3, 2, 4] ;
X = [1, 3, 4, 2] ;
X = [1, 2, 3, 4] ;
X = [3, 4, 1, 2] ;
X = [3, 1, 2, 4] ;
X = [3, 1, 4, 2] ;
false.
下面是我为实现它而编写的代码:

shuffle([], [], []).
shuffle([H|R], [H|Left], Right) :- shuffle(R, Right, Left).
shuffle([H|R], Left, [H|Right]) :- shuffle(R, Right, Left).
这很有效,甚至可以为“最一般的查询”生成合理的结果,但对于任何查询,即使是所有参数都完全实例化的查询,它也无法确定:
shuffle([1,2,3,4],[1,2],[3,4])

我真正的问题是:我能做些什么,同时保持纯洁性(因此,没有切割),这使得这个谓词在所有参数都完全实例化时具有确定性


当我在这里的时候,我对Prolog还是个新手,我想知道是否有人对我为什么会关心决定论有什么建议。它对真正的prolog程序重要吗?

不,在保持纯代码的同时,无法使该谓词具有确定性。要看到这一点,请考虑:

?- shuffle([1, 1], [1], [1]).
      true
   ;  true.
对此有两个答案。为什么?最好不要使用调试器来理解这一点,而是使用通用查询

?- shuffle([X1, X2], [Y1], [Y2]).
      X1 = Y1, X2 = Y2
   ;  X1 = Y2, X2 = Y1.
因此,在这里您可以看到参数之间的“真实”联系!现在我们的特定查询是这个更一般查询的一个实例。因此,没有办法消除这两个答案

但是,您可以以纯粹的方式使用cut,前提是对其进行保护,以使结果始终是纯粹的。就像测试
ground(shuffe(Xs,Ys,Zs))
但所有这些都是非常特别的


再想一想,可能会有一个纯粹的、确定的答案,但前提是
的答案会改变([X1,X2],[Y1],[Y2])。
。答案其实应该是:

?- shuffledet([X1, X2], [Y1], [Y2]).
      X1 = X2, X2 = Y1, Y1 = Y2      % all equal
   ;  dif(X1, X2), X1 = Y1, X2 = Y2
   ;  dif(X1, X2), X1 = Y2, X2 = Y1.

所以这可能是一种可能性。。。我会尽快悬赏500美元,但没有回应。我再次尝试另一个。

使
det
版本的
shuffle
的方法是使用库模块
reif
中的
if\u3

shuffle_det1(A、B、C):-
如果_u2;(B=[],A=C,
如果_uz(C=[],A=B,
(B=[BH | BT],C=[CH | CT],A=[AH | AT](
AH=BH,洗牌_det1(AT,BT,C)
;
AH=CH,shuffle_det1(AT,B,CT))。
在位置上工作是可以的,并且确实消除了一些(大多数?)虚假的选择点:

40?-shuffle_det1(X[1,2],[3,4])。
X=[1,2,3,4];
X=[1,3,2,4];
X=[1,3,4,2];
X=[3,1,2,4];
X=[3,1,4,2];
X=[3,4,1,2]。
41?-shuffle_det1(X[11,12],[11,22])。
X=[11,12,11,22];
X=[11,11,12,22];
X=[11,11,22,12];
X=[11,11,12,22];
X=[11,11,22,12];
X=[11,22,11,12]。
81?-shuffle_det1([1,2,3,4],[3,4],[1,2])。
对。
但是:

82?-shuffle_det1([1,2,3,4],[1,2],[3,4])。
是的;
错。
此外,正如[user:]指出的,如果两个列表的头元素相等,那么答案中会有一些冗余:

1113。。B
21 22 23 ..       C
11 (12.. + 21..)        |    21 (11.. + 22..)
12 (13.. + 21..)             11 (12.. + 22..) *
| 21 (12.. + 22..) *         | 22 (11.. + 23..)
在这里,当
11==21
时,标有
*
的两个案例实际上合并了。为了解决这一问题,在这种情况下,我们通过连续两次“展开”拣选:

shuffle_det(A、B、C):-
如果_u2;(B=[],A=C,
如果_uz(C=[],A=B,
(B=[BH | BT],C=[CH | CT],A=[AH | AT],
如果(\X^(dif(BH,CH),X=true;BH=CH,X=false),
(
AH=BH,shuffle_det(AT,BT,C)
;
AH=CH,shuffle_det(AT,B,CT)),
(
AH=BH,AT=[CH | A2],随机数据(A2,BT,CT)%**
;
拉两次(A、B、C)
;
拉动两次(A、C、B)
))))).
拉动_两次([BH|AT],[BH|BT],C):-%B,C保证为非空
如果_Ubt=[],AT=C,
(BT=[BH2 | B2],AT=[BH2 | A2],shuffle|u det(A2,B2,C)))。
测试:

35?-shuffle_det(A[11,12],[11,22])。
A=[11,11,12,22];
A=[11,11,22,12];
A=[11,12,11,22];
A=[11,22,11,12]。
这已经比shuffle\u det1好得多了。但这还不完全正确:

38?-shuffle_det(A[1],[1])。
A=[1,1];
A=[1,1];
A=[1,1]。

两次
pull_
呼叫可能是罪魁祸首。不知何故,必须只有一个,它将决定是否执行另一个…

我只想指出,OPs shuffle/3是一个奇怪的shuffle:

shuffle([], [], []).
shuffle([H|R], [H|Left], Right) :- shuffle(R, Right, Left).
shuffle([H|R], Left, [H|Right]) :- shuffle(R, Right, Left).
wiki页面的洗牌是将最后一个参数移到前面。因此,左侧和右侧不会在尾部切换:

shuffle([], [], []).
shuffle([First | ShortMerge], [First | Rest], Right) :-
    shuffle(ShortMerge, Rest, Right).
shuffle( [First | ShortMerge], Left, [First | Rest]) :-
    shuffle(ShortMerge, Left, Rest).

我不知道这是否真的重要,但仅供记录。

虚假的选择点可能会在复杂的应用程序中导致严重的性能问题,这是因为过度和耗时的回溯探索路径会导致死胡同或重复或与应用程序无关的替代解决方案。您所说的“确定性”是什么意思?在我看来,结果是完全确定的。从一次运行到另一次运行,你会得到完全相同的结果。我认为更重要的是记录和理解它们是确定性的还是有多个解决方案,以及它们是从这种情况逻辑上产生的。您可以使用cuts或
one/1
使具有多个解决方案的事物只有一个,但这通常会使正确性倒退。如果
shuffle/3
在通常的模式
shuffle(-Out,+Left,+Right)
中是确定性的(即单个解决方案),我会觉得很奇怪,但是如果我需要它看起来是随机的,并生成单个解决方案,我会为此构建一个谓词并将其作为文档记录下来。注意,这里有几种确定性的含义。在文件中