Java中类似斐波那契序列的非递归解决方案是什么?

Java中类似斐波那契序列的非递归解决方案是什么?,java,algorithm,math,recursion,Java,Algorithm,Math,Recursion,给定一个函数的伪代码 f(0) = 1; f(1) = 3; f(n) = 3 * f(n - 1) - f(n - 2); // for n >= 2. 有没有一种非递归的方法可以做到这一点?是的,所有递归算法都可以转换为迭代算法。问题的递归解决方案类似于(伪代码): 由于只需记住前两项即可计算当前项,因此可以使用类似以下伪代码的代码: def f(n): if n == 0: return 1 if n == 1: return 3 return 3 *

给定一个函数的伪代码

f(0) = 1; 
f(1) = 3; 
f(n) = 3 * f(n - 1) - f(n - 2); // for n >= 2.

有没有一种非递归的方法可以做到这一点?

是的,所有递归算法都可以转换为迭代算法。问题的递归解决方案类似于(伪代码):

由于只需记住前两项即可计算当前项,因此可以使用类似以下伪代码的代码:

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
条件只是一个捕捉溢出的诡计,溢出是可以停止构建数组的点):

为您提供一个数组定义,您可以将其插入到查找函数中,如下例所示:

public static long fn (int n) {
    long lookup[] = { 1L, 3L, 8L, 21L, 55L, 144L, 377L, 987L, 2584L, 6765L,
        17711L, 46368L, 121393L, 317811L, 832040L, 2178309L, 5702887L,
        14930352L, 39088169L, 102334155L, 267914296L, 701408733L,
        1836311903L, 4807526976L, 12586269025L, 32951280099L, 86267571272L,
        225851433717L, 591286729879L, 1548008755920L, 4052739537881L,
        10610209857723L, 27777890035288L, 72723460248141L, 190392490709135L,
        498454011879264L, 1304969544928657L, 3416454622906707L,
        8944394323791464L, 23416728348467685L, 61305790721611591L,
        160500643816367088L, 420196140727489673L, 1100087778366101931L,
        2880067194370816120L, 7540113804746346429L };

    if ((n < 1) || (n > lookup.length))
        return -1L;

    return lookup[n-1];
}
现在我们需要一条主线来比较它们:

    public static void main(String args[]) {
        for (int i = 1; i < 50; i++)
            if (fn(i) != fn2(i))
                System.out.println ("BAD:  " + i + ": " + fn(i) + ", " + fn2(i)
                    + " (" + Math.abs(fn(i) - fn2(i)) + ")");
            else
                System.out.println ("GOOD: " + i + ": " + fn(i) + ", " + fn2(i));
        }
    }
这里看起来不错,还有一些:

GOOD: 26: 32951280099, 32951280099
GOOD: 27: 86267571272, 86267571272
GOOD: 28: 225851433717, 225851433717
GOOD: 29: 591286729879, 591286729879
GOOD: 30: 1548008755920, 1548008755920
GOOD: 31: 4052739537881, 4052739537881
GOOD: 32: 10610209857723, 10610209857723
GOOD: 33: 27777890035288, 27777890035288
GOOD: 34: 72723460248141, 72723460248141
GOOD: 35: 190392490709135, 190392490709135
GOOD: 36: 498454011879264, 498454011879264
但后来有些事情开始出错了:

BAD:  37: 1304969544928657, 1304969544928658 (1)
BAD:  38: 3416454622906707, 3416454622906709 (2)
BAD:  39: 8944394323791464, 8944394323791472 (8)
BAD:  40: 23416728348467685, 23416728348467705 (20)
BAD:  41: 61305790721611591, 61305790721611648 (57)
BAD:  42: 160500643816367088, 160500643816367232 (144)
BAD:  43: 420196140727489673, 420196140727490048 (375)
上述事实非常接近,并且误差中的位数与结果中的位数成正比,这表明这可能是精度损失问题

在这一点之后,公式化函数开始返回最大长值:

BAD:  44: 1100087778366101931, 922337203685477580 (177750574680624351)
BAD:  45: 2880067194370816120, 922337203685477580 (1957729990685338540)
BAD:  46: 7540113804746346429, 922337203685477580 (6617776601060868849)
然后,我们的查找功能也会崩溃,因为数字太大,无法维持很长时间:

BAD:  47: -1, 922337203685477580 (922337203685477581)
BAD:  48: -1, 922337203685477580 (922337203685477581)
BAD:  49: -1, 922337203685477580 (922337203685477581)

[Oops,我认为这是一个Perl问题。不过,代码对Java开发人员来说应该足够可读。]

这实际上只是将递归移到userland,但您可以使用:

sub f { 
    my ($n) = @_;
    my @f = (1,3);
    $f[$_] = 3 * $f[$_-1]- $f[$_-2] for 2 .. $n;
    return $f[$n];
}
当然,这需要缓存。没有必要重新计算我们已经知道的值

my @f = (1,3);
sub f { 
    my ($n) = @_;
    $f[$_] = 3 * $f[$_-1]- $f[$_-2] for @f .. $n;
    return $f[$n];
}

很简单,在Java中,解决方案如下所示:

public int f(int n) {

      int tmp;
      int a = 3;
      int b = 1;

      for (int i = 0; i < n; i++) {
          tmp = a;
          a = 3 * a - b;
          b = tmp;
      }

      return b;

}
public int f(int n){
int tmp;
INTA=3;
int b=1;
对于(int i=0;i
所有的递归解都可以转化为迭代解(反之亦然,请参见此),尽管如果递归解是尾部递归形式,则更容易


上述算法可以理解为原始递归的动态规划解决方案,它非常有效,因为它只需要在迭代的每个点保存前面的两个值。

函数是根据自身定义的,因此在某种意义上,任何实现都是递归的,除非有数学家来告诉我们
f(n)
可以在不计算
f(n-1)
f(n-2)
的情况下进行计算。如其他人所示,有一种方法可以在不调用自身的Java函数中实现它。

如果您的问题是是否可以找到函数的等效非递归定义,则应该搜索函数的属性

你的序列可以通过写斐波那契(没有前两个数字)并删除每第二个数字:1,3,8,21,55,144

sqrt5 = sqrt(5)
phi = ( 1 + sqrt5 ) / 2
fibonacci(n) = round( power( phi, n ) / sqrt5 ) 
f(n) = fibonacci( 2*n + 2 )

这里的答案是正确的,但是它们在O(n)中工作,而你可以在O(logn)中工作,速度是指数级的。注意

[f(n)  ] = [3 -1] [f(n-1)]
[f(n-1)]   [1  0] [f(n-2)]

设vn为向量[f(n),f(n-1)],A为上述矩阵,得到vn=avn-1,因此vn=An-1v1。使用计算矩阵A的(n-1)次方,并将其乘以v1。有关线性复发的更多信息,请参见。

根据@paxdiablo的要求,我正在回答这个问题。这是一个递归关系,可以非递归地求解,类似于另一个答案中提到的斐波那契序列。结果是(Python表示法)

然而,由于浮点精度有限,该公式很可能不适用于大n。给定的python版本在n=30时失败:

>>> print ([f(n) == 3 * f(n-1) + f(n-2) for n in range(2, 30)])
[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, False]
>>> print([f(n) for n in range(30)])
[1, 3, 10, 33, 109, 360, 1189, 3927, 12970, 42837, 141481, 467280, 1543321, 5097243, 16835050, 55602393, 183642229, 606529080, 2003229469, 6616217487, 21851881930, 72171863277, 238367471761, 787274278560, 2600190307441, 8587845200883, 28363725910090, 93679022931153, 309400794703549, 1021881407041801]

警告:我使用了“+”而不是“-”,因此公式是错误的。请参阅注释。

斐波那契数列的开头是:0,1,1,2,3,5,8,13,21,34,55……

这可以通过简单的递归关系F(n)=F(n-1)+F(n-2)来定义,对于n>1和两个初始条件,F(0)=1和F(1)=1

算法Fibonacci

//计算第n个斐波那契数

//输入:非负整数

//输出:第n个斐波那契数

1。开始菲波
2.整数n,i;

3.如果n这里只是一个代码行最少、灵活性最大的函数

您可以简单地添加任何“初始值”和任何其他递归“函数”

def fib(n):
  fibs = [1, 3]       # <--  your initial values here 
  if n == 1:
    return fibs[0]
  if n == 2:
    return fibs[:1]
  for i in range(2, n):
    fibs.append(3*fibs[-1] - fibs[-2])  # <-- your function here
  return fibs

只有尾部递归算法(以及那些可以使尾部递归的算法)可以转换为迭代算法。OP的算法不能进行迭代。但这并不意味着没有迭代算法可以做同样的事情。我不确定我是否相信你所做的区分。OP没有提供算法;他提供了一个递归定义,@paxdiablo证明了该定义可以迭代计算。@ikegami,所有递归算法都可以进行迭代,前提是您可以管理自己的数据堆栈,而不是使用函数堆栈。我猜你的意思是尾部递归更容易被编译器转换为迭代。@ikegami有一个明确的定义
sqrt5 = sqrt(5)
phi = ( 1 + sqrt5 ) / 2
fibonacci(n) = round( power( phi, n ) / sqrt5 ) 
f(n) = fibonacci( 2*n + 2 )
[f(n)  ] = [3 -1] [f(n-1)]
[f(n-1)]   [1  0] [f(n-2)]
def f(n):
    return int((13**0.5-3)/(2*13**0.5)*((3-13**0.5)/2)**n + (0.5+3/(2*13**0.5))*((3+13**0.5)/2)**n)
>>> print ([f(n) == 3 * f(n-1) + f(n-2) for n in range(2, 30)])
[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, False]
>>> print([f(n) for n in range(30)])
[1, 3, 10, 33, 109, 360, 1189, 3927, 12970, 42837, 141481, 467280, 1543321, 5097243, 16835050, 55602393, 183642229, 606529080, 2003229469, 6616217487, 21851881930, 72171863277, 238367471761, 787274278560, 2600190307441, 8587845200883, 28363725910090, 93679022931153, 309400794703549, 1021881407041801]
1. Begin Fibo
2. integer n, i;
3.    if n<=1 then
4.     return n;
5.  else
6.    F(0)<-0; F(1)<-1;
7.    for i<-2 to n do
8.     F(i)<-F(i-1)+F(i-2);
9.     F(i-2)=F(i-2);
10.    F(i-1)=F(i);
11. done
12. end if
13. end Fibo
def fib(n):
  fibs = [1, 3]       # <--  your initial values here 
  if n == 1:
    return fibs[0]
  if n == 2:
    return fibs[:1]
  for i in range(2, n):
    fibs.append(3*fibs[-1] - fibs[-2])  # <-- your function here
  return fibs
n=10
print(fib(n))

[1, 3, 8, 21, 55, 144, 377, 987, 2584, 6765]