Prolog - 生成斐波那契数列
Prolog - generate fibonacci series
我想编写谓词,为给定的 N 生成斐波那契数列。
fibon(6, X) -> X = [0,1,1,2,3,5].
我有一个谓词来生成斐波那契数列的第 N 个元素:
fib(0, 0).
fib(1, 1).
fib(N, F) :-
N > 1,
N1 is N - 1,
N2 is N - 2,
fib(N1, F1),
fib(N2, F2),
F is F1 + F2.
我试着写 fibon/2,但它不起作用:
fibon(N, [H|T]) :-
fib(N, H),
N1 is N - 1,
fibon(N1, T).
我是这样解决的:
at_the_end(X, [], [X]).
at_the_end(X, [H|T], [H|T2]) :-
at_the_end(X, T, T2).
revert([], []).
revert([H|T], Out) :-
revert(T, Out1),
at_the_end(H, Out1, Out).
fib(0, 0).
fib(1, 1).
fib(N, F) :-
N > 1,
N1 is N - 1,
N2 is N - 2,
fib(N1, F1),
fib(N2, F2),
F is F1 + F2.
fibon(0, [0]).
fibon(N, [H|T]) :-
fib(N, H),
N1 is N - 1,
fibon(N1, T).
fibonacci(In, Out) :-
fibon(In, Out1),
revert(Out1, Out).
如果您乐于颠倒序列结果的顺序,那么此方法可行:
fib(0, [0]).
fib(1, [1,0]).
fib(N, [R,X,Y|Zs]) :-
N > 1,
N1 is N - 1,
fib(N1, [X,Y|Zs]),
R is X + Y.
然后 ?- fib(15,Z).
给我 [610, 377, 233, 144, 89, 55, 34, 21, 13, 8, 5, 3, 2, 1, 1, 0]
.
加入 reverse/3
谓词很容易:
reverse([],Z,Z).
reverse([H|T],Z,A) :- reverse(T,Z,[H|A]).
你可以通过使递归谓词尾部递归来挤出更多的速度:
fib_seq(0,[0]). % <- base case 1
fib_seq(1,[0,1]). % <- base case 2
fib_seq(N,Seq) :-
N > 1,
fib_seq_(N,SeqR,1,[1,0]), % <- actual relation (all other cases)
reverse(SeqR,Seq). % <- reverse/2 from library(lists)
fib_seq_(N,Seq,N,Seq).
fib_seq_(N,Seq,N0,[B,A|Fs]) :-
N > N0,
N1 is N0+1,
C is A+B,
fib_seq_(N,Seq,N1,[C,B,A|Fs]). % <- tail recursion
首先让我们观察一下您的示例查询是否按预期工作:
?- fib_seq(6,L).
L = [0, 1, 1, 2, 3, 5, 8] ;
false.
请注意,该列表没有六个元素,就像您在 post 开头的示例中那样,而是七个。这是因为谓词从零开始计数(顺便说一句,这与添加到 post 的谓词 fibonacci/2
的行为相同)。
为了比较(与@Enigmativity 的 post 中的 fib/2
)原因,让我们从 fib_seq/2
中删除目标 reverse/2
(然后你会得到所有解决方案,除了N=0 和 N=1 倒序):
?- time((fib(50000,L),false)).
% 150,001 inferences, 0.396 CPU in 0.396 seconds (100% CPU, 379199 Lips)
false.
?- time((fib_seq(50000,L),false)).
% 150,002 inferences, 0.078 CPU in 0.078 seconds (100% CPU, 1930675 Lips)
false.
或者让fib_seq/2
保持原样,并用一个额外的目标reverse/2
来测量fib/2
(那么fib/2
解决方案中的R
对应于L
在 fib_seq/2
解决方案中):
?- time((fib(50000,L),reverse(L,R),false)).
% 200,004 inferences, 0.409 CPU in 0.409 seconds (100% CPU, 488961 Lips)
false.
?- time((fib_seq(50000,L),false)).
% 200,005 inferences, 0.088 CPU in 0.088 seconds (100% CPU, 2267872 Lips)
false.
附带说明一下,我建议您在尝试获得更大的列表时将谓词 fibonacci/2
与 posted 解决方案进行比较,比如 N > 30。
使用 scanl
只是为了好玩。和一些标准的dcgs。
:-use_module(library(clpfd)).
my_plus(X,Y,Z):-
Z#>=0,
Z#=<1000, % Max size optional
Z#=X+Y.
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
concatenation([]) --> [].
concatenation([List|Lists]) -->
list(List),
concatenation(Lists).
fib(Len,List1):-
X0=1,
length(List1,Len),
length(End,2),
MiddleLen #= Len - 3,
length(Middle,MiddleLen),
phrase(concatenation([[X0],Middle,End]), List1),
phrase(concatenation([[X0],Middle]), List2),
phrase(concatenation([Middle,End]), List3),
scanl(my_plus,List2,X0,List3).
如果您想收集斐波那契数列的前 N
个元素列表,您可以使用以下规则。请记住初始化前 2 个谓词。
fib(0, [1]).
fib(1, [1, 1]).
fib(N, L) :-
N > 1,
N1 is N - 1,
N2 is N - 2,
fib(N1, F1),
last(F1, L1),
fib(N2, F2),
last(F2, L2),
L_new is L1 + L2,
append(F1, [L_new], L).
我想编写谓词,为给定的 N 生成斐波那契数列。
fibon(6, X) -> X = [0,1,1,2,3,5].
我有一个谓词来生成斐波那契数列的第 N 个元素:
fib(0, 0).
fib(1, 1).
fib(N, F) :-
N > 1,
N1 is N - 1,
N2 is N - 2,
fib(N1, F1),
fib(N2, F2),
F is F1 + F2.
我试着写 fibon/2,但它不起作用:
fibon(N, [H|T]) :-
fib(N, H),
N1 is N - 1,
fibon(N1, T).
我是这样解决的:
at_the_end(X, [], [X]).
at_the_end(X, [H|T], [H|T2]) :-
at_the_end(X, T, T2).
revert([], []).
revert([H|T], Out) :-
revert(T, Out1),
at_the_end(H, Out1, Out).
fib(0, 0).
fib(1, 1).
fib(N, F) :-
N > 1,
N1 is N - 1,
N2 is N - 2,
fib(N1, F1),
fib(N2, F2),
F is F1 + F2.
fibon(0, [0]).
fibon(N, [H|T]) :-
fib(N, H),
N1 is N - 1,
fibon(N1, T).
fibonacci(In, Out) :-
fibon(In, Out1),
revert(Out1, Out).
如果您乐于颠倒序列结果的顺序,那么此方法可行:
fib(0, [0]). fib(1, [1,0]). fib(N, [R,X,Y|Zs]) :- N > 1, N1 is N - 1, fib(N1, [X,Y|Zs]), R is X + Y.
然后 ?- fib(15,Z).
给我 [610, 377, 233, 144, 89, 55, 34, 21, 13, 8, 5, 3, 2, 1, 1, 0]
.
加入 reverse/3
谓词很容易:
reverse([],Z,Z). reverse([H|T],Z,A) :- reverse(T,Z,[H|A]).
你可以通过使递归谓词尾部递归来挤出更多的速度:
fib_seq(0,[0]). % <- base case 1
fib_seq(1,[0,1]). % <- base case 2
fib_seq(N,Seq) :-
N > 1,
fib_seq_(N,SeqR,1,[1,0]), % <- actual relation (all other cases)
reverse(SeqR,Seq). % <- reverse/2 from library(lists)
fib_seq_(N,Seq,N,Seq).
fib_seq_(N,Seq,N0,[B,A|Fs]) :-
N > N0,
N1 is N0+1,
C is A+B,
fib_seq_(N,Seq,N1,[C,B,A|Fs]). % <- tail recursion
首先让我们观察一下您的示例查询是否按预期工作:
?- fib_seq(6,L).
L = [0, 1, 1, 2, 3, 5, 8] ;
false.
请注意,该列表没有六个元素,就像您在 post 开头的示例中那样,而是七个。这是因为谓词从零开始计数(顺便说一句,这与添加到 post 的谓词 fibonacci/2
的行为相同)。
为了比较(与@Enigmativity 的 post 中的 fib/2
)原因,让我们从 fib_seq/2
中删除目标 reverse/2
(然后你会得到所有解决方案,除了N=0 和 N=1 倒序):
?- time((fib(50000,L),false)).
% 150,001 inferences, 0.396 CPU in 0.396 seconds (100% CPU, 379199 Lips)
false.
?- time((fib_seq(50000,L),false)).
% 150,002 inferences, 0.078 CPU in 0.078 seconds (100% CPU, 1930675 Lips)
false.
或者让fib_seq/2
保持原样,并用一个额外的目标reverse/2
来测量fib/2
(那么fib/2
解决方案中的R
对应于L
在 fib_seq/2
解决方案中):
?- time((fib(50000,L),reverse(L,R),false)).
% 200,004 inferences, 0.409 CPU in 0.409 seconds (100% CPU, 488961 Lips)
false.
?- time((fib_seq(50000,L),false)).
% 200,005 inferences, 0.088 CPU in 0.088 seconds (100% CPU, 2267872 Lips)
false.
附带说明一下,我建议您在尝试获得更大的列表时将谓词 fibonacci/2
与 posted 解决方案进行比较,比如 N > 30。
使用 scanl
只是为了好玩。和一些标准的dcgs。
:-use_module(library(clpfd)).
my_plus(X,Y,Z):-
Z#>=0,
Z#=<1000, % Max size optional
Z#=X+Y.
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
concatenation([]) --> [].
concatenation([List|Lists]) -->
list(List),
concatenation(Lists).
fib(Len,List1):-
X0=1,
length(List1,Len),
length(End,2),
MiddleLen #= Len - 3,
length(Middle,MiddleLen),
phrase(concatenation([[X0],Middle,End]), List1),
phrase(concatenation([[X0],Middle]), List2),
phrase(concatenation([Middle,End]), List3),
scanl(my_plus,List2,X0,List3).
如果您想收集斐波那契数列的前 N
个元素列表,您可以使用以下规则。请记住初始化前 2 个谓词。
fib(0, [1]).
fib(1, [1, 1]).
fib(N, L) :-
N > 1,
N1 is N - 1,
N2 is N - 2,
fib(N1, F1),
last(F1, L1),
fib(N2, F2),
last(F2, L2),
L_new is L1 + L2,
append(F1, [L_new], L).