我有这个程序来生成列表的所有排列。问题是,我只需要生成连续项的绝对差值小于或等于3的排列。例如:
[2,7,5] => [2,5,7]
和[7,5,2]
。自[2 7 5]
和2-7 = -5
|-5| > 3
会出错
排列计划:
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
perm([],[]).
takeout(X,[X|R],R).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S).
permutfin(X,R):-
findall(P,perm(X,P),R).
我知道我应该在perm函数的某处添加条件,但我无法确切地知道写什么或在哪里写。
答案 0 :(得分:3)
编写排列的更直观的方法是:
takeout([X|T],X,T).
takeout([H|L],X,[H|T]) :-
takeout(L,X,T).
第一个元素是原始列表,第二个元素是选中的元素,第三个元素是没有该元素的列表。
在这种情况下,置换谓词定义为:
perm([],[]).
perm(L,[E|T]) :-
takeout(L,E,R),
perm(R,T).
这也允许尾递归,这可能意味着在大多数Prolog系统中都有重要的优化。
现在,为了只生成连续差异最多为3的排列,你可以做两件事:
天真的方式是生成并测试:在这里你让Prolog产生一个排列,但是你只有在满足某个条件时才接受它。例如:
dif3([_]).
dif3([A,B|T]) :-
D is abs(A-B),
D =< 3,
dif3([B|T]).
然后定义:
perm3(L,R) :-
perm(L,R),
dif3(R).
这种方法效率不高:对于指数量的排列,只有少数是有效的,这意味着需要大量的计算工作。例如,如果元素列表为[2,5,7,9]
,它将生成以[2,9,...]
开头的所有排列,而更智能的方法可能已经看到无论如何都不会生成有效的解决方案。
另一种更智能的方法是交错生成和测试。在这里,您只选择takeout3/4
个有效候选人的数字。您可以定义谓词takeout3(L,P,X,T).
,其中L
是原始列表,P
是前一个号码,X
所选号码和T
结果列表:
takeout3([X|T],P,X,T) :-
D is abs(X-P),
D =< 3.
takeout3([H|L],N,X,[H|T]) :-
takeout3(L,N,X,T).
现在我们可以按如下方式生成排列:
perm3([],[]).
perm3(L,[E|T]) :-
takeout(L,E,R),
perm3(R,E,T).
perm3([],_,[]).
perm3(L,O,[E|T]) :-
takeout3(L,O,E,R),
perm3(R,E,T).
请注意,我们使用两个版本的perm3
:perm3/2
和perm3/3
,第一个用于生成第一个元素(使用旧的takeout/3
)和{{ 1}}用于使用perm3/3
生成排列的其余部分。
这种方法的完整源代码是:
takeout3/4
使用takeout([X|T],X,T).
takeout([H|L],X,[H|T]) :-
takeout(L,X,T).
takeout3([X|T],P,X,T) :-
D is abs(X-P),
D =< 3.
takeout3([H|L],N,X,[H|T]) :-
takeout3(L,N,X,T).
perm3([],[]).
perm3(L,[E|T]) :-
takeout(L,E,R),
perm3(R,E,T).
perm3([],_,[]).
perm3(L,O,[E|T]) :-
takeout3(L,O,E,R),
perm3(R,E,T).
运行它:
swipl
预期的行为。
答案 1 :(得分:3)
这是另一种解决方案。我在takeout
中添加了条件,以确保相邻项目彼此相差3个以上:
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
perm([],[]).
check(_,[]).
check(X,[H|_]) :-
D is X - H,
D < 4,
D > -4.
takeout(X,[X|R],R) :-
check(X,R).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S),
check(F,R).