def f(n):
if n == 0: return 1
if n == 1: return 3
return 3 * f(n-1) - f(n-2)
因为你只需要记住前两项来计算当前项,你可以使用类似下面的伪代码:
def f(n):
if n == 0:
return 1
if n == 1:
return 3
grandparent = 1
parent = 3
for i = 2 to n:
me = 3 * parent - grandparent
grandparent = parent
parent = me
return me
使用以下 Java 代码创建长值表(即 while 条件只是捕捉溢出的偷偷摸摸的技巧,这是您可以停止构建数组的点):
class GenLookup {
public static void main(String args[]) {
long a = 1, b = 3, c;
System.out.print ("long lookup[] = { " + a + "L, " + b + "L");
c = 3 * b - a;
while ((c + a) / 3 == b) {
System.out.print (", " + c + "L");
a = b; b = c; c = 3 * b - a;
}
System.out.println (" };");
}
}
是的,所有递归算法都可以转换为迭代算法。您的问题的递归解决方案类似于(伪代码):
因为你只需要记住前两项来计算当前项,你可以使用类似下面的伪代码:
这只是先处理“递归”终止条件,然后在通常会调用自身的地方迭代。在每次迭代中,您计算当前项,然后通过祖父母和父母轮换条款。
一旦计算出当前迭代,就没有必要保留祖父母,因为它不再被使用。
事实上,可以说迭代解决方案更好(从性能的角度来看),因为项不会像在递归解决方案中那样重新计算。尽管递归解决方案 确实 有一定的优雅(递归解决方案通常如此)。
当然,就像斐波那契数列一样,您计算出的值上升得非常快,因此,如果您想要可能是最快的解决方案(您应该检查所有性能声明,包括我的),那么预先计算的查找表可能是可行的方法。
使用以下 Java 代码创建长值表(即
while
条件只是捕捉溢出的偷偷摸摸的技巧,这是您可以停止构建数组的点):为您提供一个数组定义,您可以按照以下示例将其插入查找函数:
有趣的是,WolframAlpha 提出了一种甚至不使用迭代的公式化方法。如果你去 他们的网站 并输入
f(0)=1, f(1)=3, f(n)=3f(n-1)-f(n-2)
,你会得到公式:不幸的是,它可能没有迭代那么快,因为输入值的数量有限导致可以适合 Java
long
,因为它使用浮点数。几乎可以肯定(但是,同样,您需要检查一下)比表查找慢。而且,它在数学世界中可能是完美的,在数学世界中,像非无限存储这样的现实世界限制不会发挥作用,但是,可能由于 IEEE 精度的限制,它会在更高的值
n
。以下函数等效于该表达式和查找解决方案:
现在我们需要一条主线来比较它们:
这将输出:
到这里看起来不错,还有一些:
但随后有些事情开始出错:
上面的结果非常接近,并且错误中的位数与结果中的位数成正比,这表明这可能是一个精度损失问题。
在这一点之后,公式函数开始返回最大 long 值:
然后我们的查找函数也崩溃了,因为数字太大了: