Prolog,动态编程,斐波那契数列

问题描述 投票:0回答:1

我首先要说这是我遇到的家庭作业问题,我不确定在这里是否允许这种事情,但是我不知道该去哪儿。这是我被问到的问题:

在该问题的示例代码中,您可以看到斐波那契谓词fibSimple/2,该谓词计算X的斐波那契数(自然数)。天真的递归解决方案的问题在于,您最终会多次重新计算相同的递归案例。请参阅此处以获取解释。

例如,计算fib(5)涉及为fib(2)求解三个独立的时间。动态编程方法可以解决此问题。从本质上讲,它可以归结为以fib(2)开头,然后计算fib(3),然后是fib(4)等。直到达到fib(X)。您可以将这些答案存储在列表中,而fib(X)最终会成为列表中的第一项。

您的基本情况如下所示:

fib(0,[0]).
fib(1,[1,0]).

注意fib(1)定义为[1,0]的方式。 fib(1)实际上是1,但我们保留了以前的答案列表。

我们为什么要这样做?因为要计算fib(X),我们只需要计算fib(X-1)并将前两个元素加在一起,然后将它们插入列表的开头即可。例如,从上面可以很容易地计算出fib(2,Ans)。在这种情况下,fib(2)为[1,1,0]。然后fib(3)将为[2,1,1,0],fib(4)将为[3,2,1,1,0]等....

完成上述概述的fib / 2谓词-基本情况如上所示。您需要找出在基本案例之后的一行来处理递归。

这是他们提供的示例代码

fibSimple(0,0). % fib of 0 is 0
fibSimple(1,1). % fib of 1 is 1
fibSimple(N,X) :- N>1,fibSimple(N-1,A), fibSimple(N-2,B), X is A+B.

fib(0,[0]).
fib(1,[1,0]).

我已经对此进行了几次尝试,虽然我可以肯定我的尝试最终会以绝望的错误失败,但这是我最近尝试过的方法

fib(X,[fib(X-2)+fib(X-1) | _]).

我的理由是,如果您能得到最后2个的答案,并将它们加在一起,使它们成为列表的第一个或“头”,然后用下划线表示其余的。

我的2个问题是:

[[1]我不知道/认为这个下划线会做我想做的事,并且迷失了从这里去的地方和

[[2]我甚至不知道如何运行此程序,因为fib\2谓词需要2个参数。并举例说,我想运行fib\2来找到5的斐波那契,但我不知道要把什么作为第二个参数。

prolog logic fibonacci
1个回答
0
投票

因为这是家庭作业,所以我只会概述解决方案-但它应该回答您所问的问题。

谓词与函数的不同之处在于它没有返回值。 Prolog只是告诉您是否可以派生(*)。因此,如果您仅询问fib(5)是否为真,则可获得的最佳结果是“是”。但是从1到5的斐波那契数是多少呢?那是第二个参数出现的地方。您已经知道并检查:

?- fib(5, [5, 3, 2, 1, 1, 0]).
true ;                   <--- Prolog can derive this fact. With ; I see more solutions.
false                    <--- no, there are no other solutions

或者您将第二个参数保留为变量,Prolog会告诉您该变量必须具有哪些值才能派生您的查询:

?- fib(5, X).
X = [5, 3, 2, 1, 1, 0] ;
false.

因此第二个参数包含您要查找的结果。

您还可以问诸如fib(X,Y)之类的其他查询:“我们可以得出哪些数字及其斐波那契式主机?”或fib(X, [3 | _])“哪个数字计算斐波那契数3?”。在第二种情况下,我们使用下划线表示列表的其余部分无关紧要。 (2)

那么我们如何处理fib(X,[fib(X-2)+fib(X-1) | _]).?如果我们将其添加到0和1的子句中,则可以查询所有结果:

?- fib(X,Y).
X = 0,
Y = [1] ;    <-- first solution X = 0, Y = [1]
X = 1,
Y = [1, 0] ; <-- second solution X = 1, Y = [1, 0]
Y = [fib(X-2)+fib(X-1)|_2088]. <-- third solution

[第三个解决方案只是说:以术语fib(X-2)+fib(X-1)开头的列表是有效的解决方案(_2088只是一个变量,您没有为其命名)。但是,如开头所述,该术语未评估。通过定义fib(X, [quetzovercaotl(X-1) | _]),您将获得类似的结果。

fibSimple相似,您需要一个规则来告诉Prolog如何从已经知道的事实中得出新事实。我已经为您重新格式化了fibSimple

fibSimple(N,X) :-
  N>1,
  fibSimple(N-1,A),
  fibSimple(N-2,B),
  X is A+B.

这表示如果N > 1可以导出fibSimple(N-1,A),我们可以导出fibSimple(N-2,B),可以将X设置为A + B的结果,那么我们就可以导出fibSimple(N, X)。与您编写的内容不同的是,fibSimple(N-1,A)出现在规则的正文中。同样,不对参数N-1求值。实际发生的情况是,递归在与查询3-1一起调用时会构造术语(3-1)-1)fib(3,X)。实际评估发生在算术谓词is<中。例如,递归谓词在尝试评估(3-1)-1 > 1时停止,因为1>1不为真。但是我们也没有达到基本情况fibSimple(1, 1),因为项(3-1)-11并不相同,即使它们的取值相同。

这是Prolog在简单实现中未找到斐波那契数3的原因:

?- fibSimple(3, X).
false.

is谓词完成算术求值:查询X is (3-1) -1恰好具有解X = 1。 (3)

所以fibSimple实际上必须看起来像这样:(4)

fibSimple(0,1).
fibSimple(1,1).
fibSimple(N,X) :-
    N>1,
    M1 is N -1,
    M2 is N -2,
    fibSimple(M1,A),
    fibSimple(M2,B),
    X is A+B.

对于fib,您可以将其用作模板,由于AB都在历史记录列表中,因此您仅需要一个递归调用。请注意子句的开头:如果X是新值,则它也不能是新历史记录列表。例如,头部的格式可以为fib(N, [X | Oldhistory])

祝你好运!

(1)有点简化-Prolog通常会为您提供答案替换,告诉您查询中的变量具有哪些值。还有一些处理不可导数的有限方法,但您在这里不需要。

((2)如果使用算术谓词is>,则这两个查询不适用于直接实现。处理该问题的更具声明性的方法是arithmetic constraints

(3)为了使此评估有效,is的右侧可能不包含变量。这是您需要来自(2)的算术约束的地方。

((4)另外,基本案例可以评估向下传递的算术项:

fibSimple(X, 0) :-
    0 is X.
fibSimple(X, 1) :-
    1 is X.
fibSimple(N,X) :-
    N>1,
    fibSimple(N-1,A),
    fibSimple(N-2,B),
    X is A+B.

但是效率较低,因为单个数字所占用的空间比术语100000 - 1 - 1 -1 .... -1小得多。

© www.soinside.com 2019 - 2024. All rights reserved.