简体   繁体   中英

First N Fibonacci Numbers in Prolog

I want to generate the 1st N fibonacci numbers in Prolog. That means whenever I will give N = 5, it should print : 0,1,1,2,3, But whenever I run it suppose with N =5 then it gives output as : 1,2,1,3,1,2,5.

Below is my program:

fib(0,0).
fib(1,1).
fib(F,N) :-
        N>1,
        N1 is N-1,
        N2 is N-2,
        fib(F1,N1),
        fib(F2,N2),
        F is F1+F2,
        format('~w, ',[F]).

Just so we're all on the same page, this is what happens when running your code with N = 5 :

?- fib(Fib, 5).
1, 2, 1, 3, 1, 2, 5, 
Fib = 5 ;
false.

You are trying to print results rather than computing a data structure (a list) of the results. This is almost always not the way to go in Prolog.

(As an aside, the usual order of arguments in Prolog is "inputs" first, then "outputs". I believe most Prolog programmers would expect this predicate to be fib(N, Fib) rather than fib(Fib, N) . We'll continue with your version, but it is confusing!)

One of the problems with printing intermediate results is that it can be difficult to understand which results are printed, and in which order. We can change your printing goal to be a bit more explicit about that is going on:

    format('computed fib(~w, ~w)~n',[F, N]).

This gives:

?- fib(Fib, 5).
computed fib(1, 2)
computed fib(2, 3)
computed fib(1, 2)
computed fib(3, 4)
computed fib(1, 2)
computed fib(2, 3)
computed fib(5, 5)
Fib = 5 ;
false.

As you can see, this "naive" Fibonacci computation computes intermediate results several times, which is why you get more outputs than you expected. For example (in functional notation), computing fib(4) will compute fib(2) and fib(3) , but computing fib(5) will also compute fib(3) separately.

If you remove the printing, your predicate does work correctly, for example:

?- fib(Fib, 4).
Fib = 3 .

?- fib(Fib, 5).
Fib = 5 .

?- fib(Fib, 6).
Fib = 8 .

?- fib(Fib, 7).
Fib = 13 .

There are several ways of getting the results into a list if you really want. For example, using some predicates from the SWI-Prolog library:

?- numlist(1, 20, Ns), maplist(fib, Fibs, Ns).
Ns = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
Fibs = [1, 1, 2, 3, 5, 8, 13, 21, 34|...] ;
false.

There are several problems with this: Do you really want a list? Prolog's backtracking for computing results one after the other is often superior to computing a list of results "all at once". More importantly, this naive Fibonacci implementation is very very inefficient. The above goal takes over a second on my machine.

From "Approach 3" on this answer , completely reviewed here:

At rosettacode.org , under Fibonacci/Prolog, the following quite interesting solution is given. It uses a "lazy list", which is an open list (a list where the termination (the "fin") is not [] but an unbound variable), where a "frozen goal" is attached to the list-terminating unbound variable using the predicate freeze/2 . The frozen goal is run (a "coroutine") whenever the value of that unbound variable is needed.

Proceed "bottom-up" with a "lazy list" as cache. The "lazy list" is an open list that has a frozen goal to compute the next list entry on its "fin".

Retrieving a member of the list using nth0(N,Cache,F) causes unification of the unbound "fin" with a new listbox [ | ]. This thaws the goal on the "fin", which then computes the next Fibonacci number, unifies it with arg1 of the listbox and then sets up a new frozen goal on arg2 of the listbox, the % new "fin" of the lazy list.

It is not directly evident why this works with nth0/3 , so a replacement predicate retrieve/3 which also prints debugging messages has been provided.

Example:

?- debug(fib_bll).
?- fib_bottomup_lazylist_cache(10,F,Cache).

% At this point, the cache just contains [0,1|_]: [0,1|_28196]
% At K = 0, N = 10, No unfreeze at this point
% At K = 1, N = 10, No unfreeze at this point
% At K = 2, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 0, FB = 1, FIN has been unified to [1|_28628]
% At K = 3, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 1, FB = 1, FIN has been unified to [2|_28910]
% At K = 4, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 1, FB = 2, FIN has been unified to [3|_29192]
% At K = 5, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 2, FB = 3, FIN has been unified to [5|_29474]
% At K = 6, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 3, FB = 5, FIN has been unified to [8|_29756]
% At K = 7, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 5, FB = 8, FIN has been unified to [13|_30038]
% At K = 8, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 8, FB = 13, FIN has been unified to [21|_30320]
% At K = 9, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 13, FB = 21, FIN has been unified to [34|_30602]
% At K = 10, N = 10, Will call unification with a listbox that will unfreeze the goal
% Unfrozen: FA = 21, FB = 34, FIN has been unified to [55|_30884]
% Unfrozen: FA = 21, FB = 34, FIN has been unified to [55|_30884]
% F = 55,
% Cache = [0,1,1,2,3,5,8,13,21,34,55|_31458],
% freeze(_31458,fibonacci_algorithms:bll_frozen(34,55,_31458)).

Note that for call to retrieve/3 , where K==N , the same debug message is issued twice . This is because retrieve_3(K,N,[_|More],F) is attempted first, leading to thawing, but then a rollback is issued due to K<N , and the frozen goal is reinstated. The second clause retrieve_3(N,N,[F|_],F) is then attempted, leading to the same thawing. Side-effects in Prolog: interesting.

This approach allows you to widen the cache on request, too. For example,

This approach allows you to widen the cache on request, too. For example, if I want fib(10) first, but then also fib(13) I can just reuse the cache, lengthening it:

?- 
fibb_bottomup_lazylist_cache(10,Fib10,Cache),nth0(13,Cache,Fib13).
Fib10 = 55,
Cache = [0,1,1,2,3,5,8,13,21,34,55,89,144,233|_55906],
Fib13 = 233,
freeze(_55906,fibonacci_algorithms:bll_frozen(144,233,_55906)).

Note the residual goal being printed out.

% Carve the constants fib(0) and fib(1) out of the code.

const(fib0,0).
const(fib1,1).

% :- debug(fib_bll).  % Uncomment to allow debugging printout

fib_bottomup_lazylist_cache(N,F,Cache) :-
   const(fib0,F0),
   const(fib1,F1),
   Cache=[F0,F1|Fin],
   freeze(
      Fin,
      bll_frozen(F0,F1,Fin)),
   debug(fib_bll,"At this point, the cache just contains [0,1|_]: ~q",Cache),
   % nth0(N,Cache,F).  
   retrieve(N,Cache,F).

bll_frozen(FA,FB,FIN) :-
   FC is FA + FB,
   FIN=[FC|NewFIN],
   debug(fib_bll,"Unfrozen: FA = ~d, FB = ~d, FIN has been unified to ~q",[FA,FB,FIN]),
   freeze(
      NewFIN,
      bll_frozen(FB,FC,NewFIN)).

% A replacement for nth0/3 to show what's going on

retrieve(N,Cache,F) :-
   retrieve_2(0,N,Cache,F).

retrieve_2(K,N,Cache,F) :-
   (var(Cache)
    -> debug(fib_bll,"At K = ~d, N = ~d, Will call unification with a listbox that will unfreeze the goal",[K,N])
    ;  debug(fib_bll,"At K = ~d, N = ~d, No unfreeze at this point",[K,N])),
   retrieve_3(K,N,Cache,F).

retrieve_3(K,N,[_|More],F) :-
   K < N,
   !,
   Kp is K+1,
   retrieve_2(Kp,N,More,F).
retrieve_3(N,N,[F|_],F).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM