我最近开始学习Prolog,我得到了一个编写谓词list(N, L)
的任务,该谓词生成列表L,以便:
作者说有N!这样的清单。
例如,对于N = 3,所有解都是:
?- list(3, L).
L = [1, 1, 2, 2, 3, 3] ;
L = [1, 1, 2, 3, 3, 2] ;
L = [1, 2, 2, 1, 3, 3] ;
L = [1, 2, 2, 3, 3, 1] ;
L = [1, 2, 3, 3, 2, 1] ;
L = [1, 2, 3, 1, 2, 3] ;
false.
我目前的解决方案如下:
even_distance(H, [H | _]) :-
!.
even_distance(V, [_, _ | T]) :-
even_distance(V, T).
list(N, [], _, Length, _, _) :-
Length =:= 2*N,
!.
list(N, [New | L], Max, Length, Used, Duplicates) :-
select(New, Duplicates, NewDuplicates),
even_distance(New, Used),
NewLength is Length + 1,
list(N, L, Max, NewLength, [New | Used], NewDuplicates).
list(N, [New | L], Max, Length, Used, Duplicates) :-
Max < N,
New is Max + 1,
NewLength is Length + 1,
list(N, L, New, NewLength, [New | Used], [New | Duplicates]).
list(N, L) :-
list(N, L, 0, 0, [], []).
它做了两件事:
它有效,但速度慢,看起来不太好。
本练习的作者表明,对于N&lt; 12,他的解决方案生成一个平均约11个推断的单个列表(使用time/1
并将结果除以N!)。随着我的解决方案,它增长到~60。
我有两个问题:
[1, 1, 2, 2, ..., n, n]
的类似问题(例如Langford配对),但是找不到这样的东西。我问,因为最初的问题是关于在自相交的闭合曲线中枚举交点。您绘制这样的曲线,选择一个点和方向并跟随曲线,在第一次遇到时枚举每个交叉点并在第二次会议上重复该数字:example(答案为[1, 2, 3, 4, 5, 3, 6, 7, 8, 1, 9, 5, 4, 6, 7, 9, 2, 8]
)。
作者指出,每条这样的曲线都满足谓词list
,但不是每个列表都对应一条曲线。
答案 0 :(得分:2)
我不得不求助于算术来满足由偶数元素分隔的整数对的要求。很高兴能够在没有算术的情况下解决...
list(N,L) :- numlist(1,N,H), list_(H,L), even_(L). list_([D|Ds],[D|Rs]) :- list_(Ds,Ts), select(D,Rs,Ts). list_([],[]). even_(L) :- forall(nth0(P,L,X), (nth0(Q,L,X), abs(P-Q) mod 2 =:= 1)).
select / 3用于'插入模式'。
编辑以避免算术,我们可以使用这个更详细的架构
even_(L) :-
maplist(even_(L),L).
even_(L,E) :-
append(_,[E|R],L),
even_p(E,R).
even_p(E,[E|_]).
even_p(E,[_,_|R]) :- even_p(E,R).
修改的
这是一个基于预先列出的空“插槽”列表中的分配的片段。根据我的测试,它比你的解决方案更快 - 大约2倍。
list(N,L) :-
N2 is N*2,
length(L,N2),
numlist(1,N,Ns),
pairs(Ns,L).
pairs([N|Ns],L) :- first(N,L,R),even_offset(N,R),pairs(Ns,L).
pairs([],_).
first(N,[N|R],R) :- !.
first(N,[_|R],S) :- first(N,R,S).
even_offset(N,[N|_]).
even_offset(N,[_,_|R]) :- even_offset(N,R).
我的第一次尝试,在每次插入后使用even_ / 1 进行过滤,速度要慢得多。我最初专注于在select / 3之后立即推动过滤器,并且性能确实几乎是最后一个片段,但是唉,它失去了6个解决方案......