Prolog - generate fibonacci series

10,303

Solution 1

You can squeeze out a little more speed by making the recursive predicate tail recursive:

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

First let's observe that your example query works as expected:

?- fib_seq(6,L).
L = [0, 1, 1, 2, 3, 5, 8] ;
false.

Note that the list doesn't have six elements, as in your example at the beginning of your post, but seven. This is because the predicate starts counting at zero (BTW this is the same behaviour as that of the predicate fibonacci/2 that you added to your post).

For comparison (with fib/2 from @Enigmativity's post) reasons, let's either remove the goal reverse/2 from fib_seq/2 (then you'd get all solutions except N=0 and N=1 in reverse order):

?- 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.

Or leave fib_seq/2 as it is and measure fib/2 with an additional goal reverse/2 (then R in the fib/2 solution corresponds to L in the fib_seq/2 solution):

?- 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.

On a side note, I would urge you to compare your predicate fibonacci/2 with the posted solutions when trying to get bigger lists, say N > 30.

Solution 2

If you're happy to reverse the order of the results for the sequence then this works:

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.

Then ?- fib(15,Z). gives me [610, 377, 233, 144, 89, 55, 34, 21, 13, 8, 5, 3, 2, 1, 1, 0].

It would be easy to throw in a reverse/3 predicate:

reverse([],Z,Z).
reverse([H|T],Z,A) :- reverse(T,Z,[H|A]).

Solution 3

Just for fun using scanl. and some standard 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).
Share:
10,303
user
Author by

user

Updated on June 04, 2022

Comments

  • user
    user almost 2 years

    I want to write predicate that generates the Fibonacci series for given N.

    fibon(6, X) -> X = [0,1,1,2,3,5].
    

    I have a predicate to generate the N-th element of the Fibonacci series:

    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.
    

    And I try to write fibon/2, but it doesn't work:

    fibon(N, [H|T]) :-
        fib(N, H),
        N1 is N - 1,
        fibon(N1, T).
    

    I solved it like the following:

    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).