Java斐波那契数列快速方法

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

我需要为我的 Java 独立项目找到斐波那契数列的任务。以下是查找方法。

private static long getFibonacci(int n) {
    switch (n) {
        case 0:
            return 0;
        case 1:
            return 1;
        default:
            return (getFibonacci(n-1)+getFibonacci(n-2));
    }
}

private static long getFibonacciSum(int n) {
    long result = 0;

    while(n >= 0) {
        result += getFibonacci(n);
        n--;
    }
    return result;
}

private static boolean isInFibonacci(long n) {
    long a = 0, b = 1, c = 0;

    while (c < n) {
        c = a + b;
        a = b;
        b = c;
    }

    return c == n;
}

主要方法如下:

    long key = getFibonacciSum(n);
    System.out.println("Sum of all Fibonacci Numbers until Fibonacci[n]: "+key);

    System.out.println(getFibonacci(n)+" is Fibonacci[n]");

    System.out.println("Is n2 in Fibonacci Sequence ?: "+isInFibonacci(n2));

代码已完全完成并可以运行。但是如果 n 或 n2 大于正常值(斐波那契数列中的第 50 个数字)?代码将被耗尽。有什么建议吗?

java fibonacci
7个回答
8
投票

有一种方法可以使用比奈公式

即时计算斐波那契数

算法:

function fib(n):
   root5 = squareroot(5)
   gr = (1 + root5) / 2
   igr = 1 - gr
   value = (power(gr, n) - power(igr, n)) / root5

   // round it to the closest integer since floating 
   // point arithmetic cannot be trusted to give
   // perfect integer answers. 
   return floor(value + 0.5) 

一旦执行此操作,您需要了解您正在使用的编程语言及其行为方式。这可能会返回浮点十进制类型,而可能需要整数。

该解决方案的复杂度为 O(1)。


3
投票

是的,您可以做的一项改进是

getFibonacciSum()
:您不必一次又一次地调用
isInFibonacci
从头开始重新计算所有内容,您可以执行与
isInFibonacci
完全相同的操作并将总和输入一次通过,类似:

private static int getFibonacciSum(int n) {
    int a = 0, b = 1, c = 0, sum = 0;

    while (c < n) {
        c = a + b;
        a = b;
        sum += b;
        b = c;            
    }   
    sum += c;    
    return sum;
}

2
投票

使用传统的递归解决方案:内存不是问题,但例如获取 fib(1000000) 需要大量时间。

使用以下解决方案:对于非常大的输入,例如 fib(99999*(10^999999)),我们可能会耗尽内存,但它比传统解决方案快得多。该解决方案基于地图和一些数学公式的使用(来源:https://www.nayuki.io/page/fast-fibonacci-algorithms)。

数学公式:

F(2k) = F(k)[2F(k+1)−F(k)]

F(2k+1) = F(k+1)^2+F(k)^2

解决方案:

public BigInteger fib(BigInteger n) {
    if (n.equals(BigInteger.ZERO))
        return BigInteger.ZERO;
    if (n.equals(BigInteger.ONE) || n.equals(BigInteger.valueOf(2)))
        return BigInteger.ONE;
    
    BigInteger index = n;
    
    //we could have 2 Lists instead of a map
    Map<BigInteger,BigInteger> termsToCalculate = new TreeMap<BigInteger,BigInteger>();
    
    //add every index needed to calculate  index n
    populateMapWhitTerms(termsToCalculate, index);
    
    termsToCalculate.put(n,null); //finally add n to map
    
    Iterator<Map.Entry<BigInteger, BigInteger>> it = termsToCalculate.entrySet().iterator();//it 
    it.next(); //it = key number 1, contains fib(1);
    it.next(); //it = key number 2, contains fib(2);
    
    //map is ordered
    while (it.hasNext()) {
        Map.Entry<BigInteger, BigInteger> pair = (Entry<BigInteger, BigInteger>)it.next();//first it = key number 3
        index = (BigInteger) pair.getKey();
        
        if(index.remainder(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) {
            //index is divisible by 2
            //F(2k) = F(k)[2F(k+1)−F(k)]
            pair.setValue(termsToCalculate.get(index.divide(BigInteger.valueOf(2))).multiply(
                    (((BigInteger.valueOf(2)).multiply(
                            termsToCalculate.get(index.divide(BigInteger.valueOf(2)).add(BigInteger.ONE)))).subtract(
                                    termsToCalculate.get(index.divide(BigInteger.valueOf(2)))))));
        }
        else {
            //index is odd
            //F(2k+1) = F(k+1)^2+F(k)^2
            pair.setValue((termsToCalculate.get(index.divide(BigInteger.valueOf(2)).add(BigInteger.ONE)).multiply(
                    termsToCalculate.get(index.divide(BigInteger.valueOf(2)).add(BigInteger.ONE)))).add(
                            (termsToCalculate.get(index.divide(BigInteger.valueOf(2))).multiply(
                            termsToCalculate.get(index.divide(BigInteger.valueOf(2))))))
                    );
        }
    }
    
    // fib(n) was calculated in the while loop
    return termsToCalculate.get(n);
}

private void populateMapWhitTerms(Map<BigInteger, BigInteger> termsToCalculate, BigInteger index) {
    if (index.equals(BigInteger.ONE)) { //stop
        termsToCalculate.put(BigInteger.ONE, BigInteger.ONE);
        return;
        
    } else if(index.equals(BigInteger.valueOf(2))){
        termsToCalculate.put(BigInteger.valueOf(2), BigInteger.ONE);
        return;
        
    } else if(index.remainder(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) {
        // index is divisible by 2
        // FORMUMA: F(2k) = F(k)[2F(k+1)−F(k)]
                    
        // add F(k) key to termsToCalculate (the key is replaced if it is already there, we are working with a map here)
        termsToCalculate.put(index.divide(BigInteger.valueOf(2)), null);
        populateMapWhitTerms(termsToCalculate, index.divide(BigInteger.valueOf(2)));

        // add F(k+1) to termsToCalculate
        termsToCalculate.put(index.divide(BigInteger.valueOf(2)).add(BigInteger.ONE), null);
        populateMapWhitTerms(termsToCalculate, index.divide(BigInteger.valueOf(2)).add(BigInteger.ONE));
        
    } else {
        // index is odd
        // FORMULA: F(2k+1) = F(k+1)^2+F(k)^2

        // add F(k+1) to termsToCalculate
        termsToCalculate.put(((index.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2)).add(BigInteger.ONE)),null);
        populateMapWhitTerms(termsToCalculate,((index.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2)).add(BigInteger.ONE)));

        // add F(k) to termsToCalculate
        termsToCalculate.put((index.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2)), null);
        populateMapWhitTerms(termsToCalculate, (index.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2)));
    }
    
}

1
投票

这种求解方法称为动态规划

  1. 在这个方法中我们记住了之前的结果
  2. 所以当递归发生时,CPU不需要做任何工作来一次又一次地重新计算相同的值

    class fibonacci
    {
    static int fib(int n)
     {
    /* Declare an array to store Fibonacci numbers. */
       int f[] = new int[n+1];
       int i;
    
       /* 0th and 1st number of the series are 0 and 1*/
       f[0] = 0;
       f[1] = 1;
    
       for (i = 2; i <= n; i++)
       {
           /* Add the previous 2 numbers in the series
            and store it */
           f[i] = f[i-1] + f[i-2];
        }
    
        return f[n];
      }
    
    public static void main (String args[])
        {
           int n = 9;
           System.out.println(fib(n));
        }
    }
    

0
投票
public static long getFib(final int index) {
        long a=0,b=0,total=0;
        for(int i=0;i<= index;i++) {
                if(i==0) {
                     a=0;
                     total=a+b;
                 }else if(i==1) {
                     b=1;
                     total=a+b;
                 }

            else if(i%2==0) {
                total = a+b;
                a=total;                
            }else {
                total = a+b;
                b=total;
            }

        }
        return total;
    }

0
投票

我已经检查了所有解决方案,对我来说,最快的解决方案是使用流,并且可以轻松修改此代码以收集所有斐波那契数。

 public static Long fibonaciN(long n){
    return Stream.iterate(new long[]{0, 1}, a -> new long[]{a[1], a[0] + a[1]})
            .limit(n)
            .map(a->a[0])
            .max(Long::compareTo)
            .orElseThrow();
}

-1
投票

50 或略低于 50 是直接递归实现所能达到的极限。如果您想要更高的水平,您可以切换到迭代或动态编程 (DP) 方法。我建议从中了解这些内容:https://www.javacodegeeks.com/2014/02/dynamic-programming-introduction.html。并且不要忘记查看 David 评论中的解决方案,真正有效。这些链接显示了如何使用 DP 方法即时计算

n = 500000
。该链接还解释了“记忆化”的概念,通过存储中间(但稍后可重新调用)结果来加速计算。

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