【问题标题】:First N Fibonacci Numbers in PrologProlog 中的前 N ​​个斐波那契数
【发布时间】:2020-10-17 16:10:43
【问题描述】:

我想在 Prolog 中生成第一个 N 个斐波那契数。这意味着每当我给出 N = 5 时,它应该打印:0,1,1,2,3,但是每当我假设 N =5 运行它时,它会给出输出:1,2,1,3,1, 2,5。

下面是我的程序:

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

【问题讨论】:

  • 这会计算 F = fib(N),而不是前 N 个数字 fib(.)。您可以通过调用目标 bagof(F,X^(between(0,5,X),fib(F,X)),Bag). 来获取前 N 个 fib(.) 数字的列表,但在这里寻找计算成本较低的 fib/2Prolog, Dynamic Programming, Fibonacci series
  • @David Tonhofer :我看到了那个链接,但是他们正在计算第 n 个斐波那契数。例如,如果我把 N=5 放在那里,我得到的输出是 = 5 而不是 1,1,2,3,5
  • 代替我之前参考的文字墙,我建立了一个很好的可能实现树here。选择一个!

标签: prolog fibonacci


【解决方案1】:

所以我们都在同一个页面上,这就是使用 N = 5 运行代码时会发生的情况:

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

您正在尝试打印结果,而不是计算结果的数据结构(列表)。这在 Prolog 中几乎总是不会采用的方式。

(顺便说一句,Prolog 中参数的通常顺序是首先“输入”,然后是“输出”。我相信大多数 Prolog 程序员会期望这个谓词是 fib(N, Fib) 而不是 fib(Fib, N)。我们将继续与你的版本,但它是令人困惑的!)

打印中间结果的一个问题是很难理解打印哪些结果以及打印顺序。我们可以将您的打印目标更改为更明确地说明正在发生的事情:

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

这给出了:

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

如您所见,这种“幼稚”的斐波那契计算会多次计算中间结果,这就是为什么您会得到比预期更多的输出。例如(以函数表示法),计算fib(4) 将计算fib(2)fib(3),但计算fib(5)分别计算fib(3)

如果您删除打印,您的谓词确实可以正常工作,例如:

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

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

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

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

如果您确实需要,有多种方法可以将结果放入列表中。例如,使用 SWI-Prolog 库中的一些谓词:

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

这有几个问题:你真的想要一个列表吗? Prolog 一个接一个地计算结果的回溯通常优于“一次”计算结果列表。更重要的是,这种幼稚的斐波那契实现非常低效。上述目标在我的机器上占用了一秒钟。

【讨论】:

    【解决方案2】:

    来自this answer 上的“方法 3”,在此处进行了完整评论:

    rosettacode.org, 在 Fibonacci/Prolog 下,以下相当有趣的解决方案是 给定的。它使用一个“惰性列表”,它是一个开放列表(一个列表,其中 终止(“fin”)不是[],而是一个未绑定的变量),其中 使用“冻结目标”附加到列表终止的未绑定变量 谓词 freeze/2。 每当它的值时,都会运行冻结目标(“协程”) 需要未绑定的变量。

    使用“惰性列表”作为缓存继续“自下而上”。 “懒人名单” 是一个开放列表,它有一个冻结的目标来计算下一个列表 进入它的“鳍”。

    使用nth0(N,Cache,F) 检索列表成员会导致未绑定的“fin”与新列表框[|] 统一。这 解冻“鳍”上的目标,然后计算下一个 斐波那契数,将其与列表框的 arg1 统一起来,然后 在列表框的 arg2 上设置一个新的冻结目标,即 惰性列表的 % 新“fin”。

    目前尚不清楚为什么这适用于nth0/3,因此提供了一个替换谓词retrieve/3,它也打印调试消息。

    例子:

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

    注意调用retrieve/3,其中K==N,同样的调试 消息发出两次。这是因为 retrieve_3(K,N,[_|More],F) 首先尝试,导致 解冻,但随后由于K<N 而发出回滚,并且 冻结的目标被恢复。第二条 然后尝试retrieve_3(N,N,[F|_],F),导致相同的结果 解冻。 Prolog 中的副作用:很有趣。

    这种方法也允许您根据请求扩大缓存。 例如,

    这种方法也允许您根据请求扩大缓存。为了 例如,如果我首先想要 fib(10),然后也想要 fib(13),我可以重复使用缓存,延长它:

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

    注意打印出的剩余目标。

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

    【讨论】:

    • 我还创建了一个斐波那契计算概览here
    • 算法已经彻底改版了,因为我自己都看不懂了。
    猜你喜欢
    • 2022-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-18
    • 2013-04-15
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多