如何使用dif / 2防止生成序列中的重复?

时间:2014-12-06 19:11:24

标签: prolog clpfd failure-slice prolog-dif

这个问题在回答another question on StackOverflow时(概括了一下)产生了所有由有限元素组构成的序列而没有重复出现。

正如鲍里斯在评论中正确指出的那样,这个问题有许多现有的解决方案。但是,我感兴趣的是一个不使用累加器的解决方案(即,已经挑选过的元素列表,要与之比较一个新选择的元素),而是使用dif/2语句。

为了说明,在我的下面的程序中,我有4个元素,并且在4个递归调用之后,有两个div/2语句,这些语句指出到目前为止已经选择的4个元素是成对不相似的。从这一点可以推断,继续递归并寻找第五个元素是没有意义的,因为给定div/2语句没有剩余的元素。有没有办法将这种“知识”编码到程序中,以便它不再循环?

:- use_module(library(apply)).
:- use_module(library(dif)).

sequences([]).
sequences([H|T]):-
  maplist(dif(H), T),
  between(1, 4, H),
  sequences(T).

当前的循环行为:

?- sequences(X).
X = [] ;
X = [1] ;
...
X = [4, 3, 1, 2] ;
X = [4, 3, 2, 1] ;
<LOOP>

2 个答案:

答案 0 :(得分:2)

不是真正的答案,但评论的时间太长了:

您的问题是您希望将元素保留为事实。将它们放在列表中,您可以使用select/3从该列表中取出一个元素。只要你把它们作为事实,这将比它需要的更圆(我觉得)。排序列表(基数通常有订单)是在Prolog中表示集合的完美好方法。

编辑:

由于我仍然不确定我是否理解你的问题,所以这是我认为你问的问题的实际答案:

请勿使用dif/2,因为没有必要。相反,例如:

combination([], _).
combination([X|Xs], Set) :-
    select(X, Set, Set0),
    combination(Xs, Set0).

在这里,您必须使用setof/3或另一个构建所有解决方案列表的谓词来构建初始集。如果您可以使用列表并dif/2,则无法看到select/3的必要性。

但如果你真的坚持,我会这样做:

set_el(a).
set_el(b).
set_el(c).

set_el_combination(Combination) :-
    set_el_combination_1([], Combination).

set_el_combination_1(C, R) :-
    reverse(C, R).
set_el_combination_1(C0, C) :-
    maplist(dif(X), C0),
    set_el(X),
    set_el_combination_1([X|C0], C).

您会注意到解决方案的顺序是不同的(正确的词典顺序),这是预期的。如果要避免在最后反转,可以使用差异列表。我相信这也可以写成DCG。

这有帮助吗?

答案 1 :(得分:2)

开始时的小问题 - 名称:sequences/1表示序列列表(无论序列是什么),它应该是sequence/1

你要求很多糟糕的Prolog系统:你要求更强的一致性。无论如何,我猜想。

我的即时反应(使用library(clpfd)!)不起作用,让我们看看为什么

?- length(Xs,N),Xs ins 1..4, all_distinct(Xs).

它的循环次数与您的版本一样多,使用此

可以最好地看到它
?- length(Xs,N), false, Xs ins 1..4, all_distinct(Xs).

因此length/2已经错了。也许我重申你的程序,并尝试找出你的程序没有终止的原因:

sequences([]) :- false.
sequences([H|T]):-
  maplist(dif(H), T), false
  between(1, 4, H),
  sequences(T).

?- sequences(X), false.

我们最亲切的声明性海报儿童maplist/2在flagranti中抓住了 !好吧,也许我们不应该那么苛刻。毕竟,诚实的不终止谓词总是比不合理的不完整或不完整的黑客更好。

我们需要了解的是all_distinct/1要求知道列表的长度,并且所有域名也必须存在。

sequence(Xs) :-
   sequence_aux(Xs, []).

sequence_aux([], _).
sequence_aux([X|Xs], Ys) :-
   X in 1..4,
   all_distinct([X|Ys]),
   sequence_aux(Xs, [X|Ys]).

 ?- sequence(X). 

现在终止。

@mat可能会注意到all_distinct([_])可能会被删除。甚至可能更多。

如果您不喜欢此解决方案,因为它使用了额外的参数,则需要实现更安全的maplist/2

fmaplist(C_1, Xs) :-
    freeze(Xs, fmaplist_aux(C_1, Xs)).

fmaplist_aux(_C_1, []).
fmaplist_aux(C_1, [X|Xs]) :-
   call(C_1, X),
   freeze(Xs, fmaplist_aux(C_1, Xs)).

现在您可以逐字使用原始程序。但我觉得它并不擅长。了解冻结程序中非终止的精确边界要困难得多。


顺便说一句:您可能会尝试在SWI中获取正确的变量名称以进行答案替换,因为_G772 - 类似的编号不允许将答案重新粘贴回顶层shell并获得正确的结果。< / p>