我试图弄清楚我的Prolog程序中是否有无限循环,或者如果我写的不好,所以它很慢。我试图从dailyprogrammer subreddit解决square sum chains问题。给定数字N,找到数字1-N(包括端点)的排序,使得排序中的每对相邻数字的总和是完美的平方。这个最小的N是15,排序[8, 1, 15, 10, 6, 3, 13, 12, 4, 5, 11, 14, 2, 7, 9]
。这是我尝试用来解决问题的代码:
is_square(Num):- is_square_help(Num, 0).
is_square_help(Num, S):- Num =:= S * S.
is_square_help(Num, S):-
Num > S * S,
T is S+1,
is_square_help(Num, T).
is_square_help(Num, S):- Num < S * S, fail.
contains(_, []):- fail.
contains(Needle, [Needle|_]).
contains(Needle, [_|Tail]):- contains(Needle, Tail).
nums(0, []).
nums(Num, List) :- length(List, Num), nums_help(Num, List).
nums_help(0, _).
nums_help(Num, List) :-
contains(Num, List),
X is Num - 1,
nums_help(X, List).
square_sum(Num, List) :-
nums(Num, List),
square_sum_help(List).
square_sum_help([X, Y|T]) :-
Z is X + Y,
is_square(Z),
square_sum_help(T).
目前,当我运行square_sum(15, List).
时,程序不会终止。我已经独自离开了大约10分钟,它一直在运行。我知道有些问题需要很长时间才能解决,但据报道其他问题产生了毫秒级的答案。我在这里做错了什么?
答案 0 :(得分:4)
SWI-Prolog允许这种紧凑的实现
square_sum(N,L) :-
numlist(1,N,T),
select(D,T,R),
adj_squares(R,[D],L).
adj_squares([],L,R) :- reverse(L,R).
adj_squares(T,[S|Ss],L) :-
select(D,T,R),
float_fractional_part(sqrt(S+D))=:=0,
adj_squares(R,[D,S|Ss],L).
在N = 15
时非常快 按照建议编辑,按顺序构建列表会产生更好的代码:
square_sum(N,L) :-
numlist(1,N,T),
select(D,T,R),
adj_squares(R,D,L).
adj_squares([],L,[L]).
adj_squares(T,S,[S|L]) :-
select(D,T,R),
float_fractional_part(sqrt(S+D))=:=0,
adj_squares(R,D,L).
修改的
当N增长时,上面的代码变得太慢了。我改变了策略,现在尝试找到由二元关系引起的图中的哈密顿路径。对于N = 15,它看起来像
(这是生成Graphviz脚本的代码:
square_pairs(N,I,J) :-
between(1,N,I),
I1 is I+1,
between(I1,N,J),
float_fractional_part(sqrt(I+J))=:=0.
square_pairs_graph(N) :-
format('graph square_pairs_N_~d {~n', [N]),
forall(square_pairs(N,I,J), format(' ~d -- ~d;~n', [I,J])),
writeln('}').
)
这里是查找路径的代码
hamiltonian_path(N,P) :-
square_pairs_struct(N,G),
between(1,N,S),
extend_front(1,N,G,[S],P).
extend_front(N,N,_,P,P) :- !.
extend_front(Len,Tot,G,[Node|Ins],P) :-
arg(Node,G,Arcs),
member(T,Arcs),
\+memberchk(T,Ins),
Len1 is Len+1,
extend_front(Len1,Tot,G,[T,Node|Ins],P).
struct_N_of_E(N,E,S) :-
findall(E,between(1,N,_),As),
S=..[graph|As].
square_pairs_struct(N,G) :-
struct_N_of_E(N,[],G),
forall(square_pairs(N,I,J), (edge(G,I,J),edge(G,J,I))).
edge(G,I,J) :-
arg(I,G,A), B=[J|A], nb_setarg(I,G,B).
答案 1 :(得分:4)
以下是使用约束逻辑编程的解决方案:
squares_chain(N, Cs) :- numlist(1, N, Ns), phrase(nums_partners(Ns, []), NPs), group_pairs_by_key(NPs, Pairs), same_length(Ns, Pairs), pairs_values(Pairs, Partners), maplist(domain, Is0, Partners), circuit([D|Is0]), labeling([ff], Is0), phrase(chain_(D, [_|Is0]), Cs). chain_(1, _) --> []. chain_(Pos0, Ls0) --> [Pos], { Pos0 #> 1, Pos #= Pos0 - 1, element(Pos0, Ls0, E) }, chain_(E, Ls0). plus_one(A, B) :- B #= A + 1. domain(V, Ls0) :- maplist(plus_one, Ls0, Ls), foldl(union_, Ls, 1, Domain), V in Domain. union_(N, Dom0, Dom0\/N). nums_partners([], _) --> []. nums_partners([N|Rs], Ls) --> partners(Ls, N), partners(Rs, N), nums_partners(Rs, [N|Ls]). partners([], _) --> []. partners([L|Ls], N) --> ( { L + N #= _^2 } -> [N-L] ; [] ), partners(Ls, N).
示例查询和答案:
?- squares_chain(15, Cs). Cs = [9, 7, 2, 14, 11, 5, 4, 12, 13|...] ; Cs = [8, 1, 15, 10, 6, 3, 13, 12, 4|...] ; false.
更长的序列:
?- time(squares_chain(100, Cs)). 15,050,570 inferences, 1.576 CPU in 1.584 seconds (99% CPU, 9549812 Lips) Cs = [82, 87, 57, 24, 97, 72, 28, 21, 60|...] .
答案 2 :(得分:2)
你做错了主要是你在开始测试之前生成了整个列表。
调用fail
的两个条款毫无意义。删除它们不会改变程序。这样做的唯一原因是你做了一些副作用,比如打印输出。
您生成列表的代码和所有排列似乎都有效,但使用select/3
可以更简单地完成。
你似乎没有square_sum_help/1
中的基础案例,你似乎也只检查每一对,这会在几年或其他什么时候导致你的程序出现问题检查正确的订购。
所以,通过交错生成和测试,像这样
square_sum(Num,List) :-
upto(Num,[],List0),
select(X,List0,List1),
square_sum_helper(X,List1,[],List).
square_sum_helper(X1,Rest0,List0,List) :-
select(X2,Rest0,Rest),
Z is X1 + X2,
is_square(Z,0),
square_sum_helper(X2,Rest,[X1|List0],List).
square_sum_helper(_,[],List0,List) :- reverse(List0,List).
is_square(Num,S) :-
Sqr is S * S,
( Num =:= Sqr ->
true
; Num > Sqr,
T is S + 1,
is_square(Num,T) ).
upto(N,List0,List) :-
( N > 0 ->
M is N - 1,
upto(M,[N|List0],List)
; List = List0 ).
正确的结果是在9毫秒左右产生的(SWI Prolog)。
?- ( square_sum(15,List), write(List), nl, fail ; true ).
[8,1,15,10,6,3,13,12,4,5,11,14,2,7,9]
[9,7,2,14,11,5,4,12,13,3,6,10,15,1,8]
?- time(square_sum(15,_)).
% 37,449 inferences, 0.009 CPU in 0.009 seconds (100% CPU, 4276412 Lips)
编辑:修正了一些错别字。
答案 3 :(得分:1)
contains/2
:
条款contains(_, []):- fail.
充其量也是多余的。
您应该输入正文!, fail.
但是不需要它,因为不应该提到无法提供的东西(封闭的世界假设)。
实际上{p}contains/2
member/2
(内置)