web-dev-qa-db-ja.com

Java再帰フィボナッチ数列

この簡単なコードを説明してください:

public int fibonacci(int n)  {
    if(n == 0)
        return 0;
    else if(n == 1)
      return 1;
   else
      return fibonacci(n - 1) + fibonacci(n - 2);
}

特にn = 5の場合、fibonacci(4)+ fibonacci(3)が呼び出されるなどの理由で最後の行と混同しますが、このアルゴリズムがインデックス5の値をどのように計算するかはわかりません。方法。詳しく説明してください!

141
Index Hacker

フィボナッチ数列では、各項目は前の2つの合計です。だから、あなたは再帰的なアルゴリズムを書いた。

そう、

fibonacci(5) = fibonacci(4) + fibonacci(3)

fibonacci(3) = fibonacci(2) + fibonacci(1)

fibonacci(4) = fibonacci(3) + fibonacci(2)

fibonacci(2) = fibonacci(1) + fibonacci(0)

今、あなたはすでにfibonacci(1)==1 and fibonacci(0) == 0を知っています。そのため、後で他の値を計算できます。

今、

fibonacci(2) = 1+0 = 1
fibonacci(3) = 1+1 = 2
fibonacci(4) = 2+1 = 3
fibonacci(5) = 3+2 = 5

そしてfibonacciシーケンス0,1,1,2,3,5,8,13,21....から、5th elementに対してfibonacciシーケンスは5を返すことがわかります。

再帰チュートリアル についてはこちらを参照してください。

154
RanRag

コードには2つの問題があります。

  1. 結果はintに格納されます。これは最初の48個のフィボナッチ数のみを処理できます。この後、整数のfillマイナスビットで結果が間違っています。
  2. しかし、fibonacci(50)を実行することはできません。
    コード
    fibonacci(n - 1) + fibonacci(n - 2)
    は非常に間違っています。
    問題は、それがフィボナッチを50回ではなくもっと多く呼ぶということです。
    最初はfibonacci(49)+ fibonacci(48)と呼ばれます。
    次のフィボナッチ(48)+フィボナッチ(47)およびフィボナッチ(47)+フィボナッチ(46)
    フィボナッチ(n)が悪化するたびに、複雑さは指数関数的になります。 enter image description here

非再帰的コードへのアプローチ:

 double fibbonaci(int n){
    double prev=0d, next=1d, result=0d;
    for (int i = 0; i < n; i++) {
        result=prev+next;
        prev=next;
        next=result;
    }
    return result;
}
50
chro

擬似コードでは、n = 5の場合、次のようになります。

フィボナッチ(4)+フィボナッチ(3)

これは次のように分類されます。

(fibonacci(3)+ fibonnacci(2))+(fibonacci(2)+ fibonnacci(1))

これは次のように分類されます。

((((fibonacci(2)+ fibonnacci(1))+((fibonacci(1)+ fibonnacci(0))))+((((fibonacci(1)+ fibonnacci(0))+ 1))

これは次のように分類されます。

((((((fibonacci(1)+ fibonnacci(0))+ 1)+ 1)+(1 + 0))+((1 + 0)+ 1)))

これは次のように分類されます。

(((((1 + 0)+ 1)+((1 + 0))+((1 + 0)+ 1)))

5

フィボナッチ数列が1 1 2 3 5 8 ...であるとすると、5番目の要素は5になります。繰り返し.

35
Dan Hardiker

再帰は時々把握するのが難しい場合があります。一枚の紙でそれを少しだけ評価してください。

fib(4)
-> fib(3) + fib(2)
-> fib(2) + fib(1) + fib(1) + fib(0)
-> fib(1) + fib(0) + fib(1) + fib(1) + fib(0)
-> 1 + 0 + 1 + 1 + 0
-> 3

Javaが実際にこれをどのように評価するかはわかりませんが、結果は同じになります。

11
tim

以下のように、機能を単純化することもできます。

public int fibonacci(int n)  {
    if (n < 2) return n;

    return fibonacci(n - 1) + fibonacci(n - 2);
}
9
Otavio Ferreira
                                F(n)
                                /    \
                            F(n-1)   F(n-2)
                            /   \     /      \
                        F(n-2) F(n-3) F(n-3)  F(n-4)
                       /    \
                     F(n-3) F(n-4)

注意すべき重要な点は、このアルゴリズムは前の計算値の結果を格納しないため指数関数的であるということです。例えばF(n-3)は3回呼ばれます。

詳細はdasguptaの章0.2のアルゴリズムを参照してください。

8
Amandeep Kamboj

答えのほとんどは良いもので、フィボナッチの再帰がどのように機能するのかを説明しています。

これは再帰を含む3つのテクニックの分析です。

  1. forループ
  2. 再帰
  3. メモ

これが3つすべてをテストするためのコードです。

public class Fibonnaci {
    // Output = 0 1 1 2 3 5 8 13

    static int fibMemo[];

    public static void main(String args[]) {
        int num = 20;

        System.out.println("By For Loop");
        Long startTimeForLoop = System.nanoTime();
        // returns the fib series
        int fibSeries[] = fib(num);
        for (int i = 0; i < fibSeries.length; i++) {
            System.out.print(" " + fibSeries[i] + " ");
        }
        Long stopTimeForLoop = System.nanoTime();
        System.out.println("");
        System.out.println("For Loop Time:" + (stopTimeForLoop - startTimeForLoop));


        System.out.println("By Using Recursion");
        Long startTimeRecursion = System.nanoTime();
        // uses recursion
        int fibSeriesRec[] = fibByRec(num);

        for (int i = 0; i < fibSeriesRec.length; i++) {
            System.out.print(" " + fibSeriesRec[i] + " ");
        }
        Long stopTimeRecursion = System.nanoTime();
        System.out.println("");
        System.out.println("Recursion Time:" + (stopTimeRecursion -startTimeRecursion));



        System.out.println("By Using Memoization Technique");
        Long startTimeMemo = System.nanoTime();
        // uses memoization
        fibMemo = new int[num];
        fibByRecMemo(num-1);
        for (int i = 0; i < fibMemo.length; i++) {
            System.out.print(" " + fibMemo[i] + " ");
        }
        Long stopTimeMemo = System.nanoTime();
        System.out.println("");
        System.out.println("Memoization Time:" + (stopTimeMemo - startTimeMemo));

    }


    //fib by memoization

    public static int fibByRecMemo(int num){

        if(num == 0){
            fibMemo[0] = 0;
            return 0;
        }

        if(num ==1 || num ==2){
          fibMemo[num] = 1;
          return 1; 
        }

        if(fibMemo[num] == 0){
            fibMemo[num] = fibByRecMemo(num-1) + fibByRecMemo(num -2);
            return fibMemo[num];
        }else{
            return fibMemo[num];
        }

    }


    public static int[] fibByRec(int num) {
        int fib[] = new int[num];

        for (int i = 0; i < num; i++) {
            fib[i] = fibRec(i);
        }

        return fib;
    }

    public static int fibRec(int num) {
        if (num == 0) {
            return 0;
        } else if (num == 1 || num == 2) {
            return 1;
        } else {
            return fibRec(num - 1) + fibRec(num - 2);
        }
    }

    public static int[] fib(int num) {
        int fibSum[] = new int[num];
        for (int i = 0; i < num; i++) {
            if (i == 0) {
                fibSum[i] = i;
                continue;
            }

            if (i == 1 || i == 2) {
                fibSum[i] = 1;
                continue;
            }

            fibSum[i] = fibSum[i - 1] + fibSum[i - 2];

        }
        return fibSum;
    }

}

結果は次のとおりです。

By For Loop
 0  1  1  2  3  5  8  13  21  34  55  89  144  233  377  610  987  1597  2584  4181 
For Loop Time:347688
By Using Recursion
 0  1  1  2  3  5  8  13  21  34  55  89  144  233  377  610  987  1597  2584  4181 
Recursion Time:767004
By Using Memoization Technique
 0  1  1  2  3  5  8  13  21  34  55  89  144  233  377  610  987  1597  2584  4181 
Memoization Time:327031

したがって、メモ化は時間的に最善の方法であり、forループの一致が厳密に一致することがわかります。

しかし、再帰は最も時間がかかり、現実の世界では避けるべきです。また、再帰を使用している場合は、必ず解を最適化してください。

7
Pritam Banerjee

フィボナッチ再帰的解法では、より大きな数の値を検索しながら、より小さなフィボナッチ数の出力を保存することが重要です。これを「メモ化」と呼びます。

これは小さなフィボナッチ値を記憶しながら大きなフィボナッチ数を取得するコードです。このコードは効率的で、同じ機能を複数回要求することはありません。

import Java.util.HashMap;

public class Fibonacci {
  private HashMap<Integer, Integer> map;
  public Fibonacci() {
    map = new HashMap<>();
  }
  public int findFibonacciValue(int number) {
    if (number == 0 || number == 1) {
      return number;
    }
    else if (map.containsKey(number)) {
      return map.get(number);
    }
    else {
      int fibonacciValue = findFibonacciValue(number - 2) + findFibonacciValue(number - 1);
      map.put(number, fibonacciValue);
      return fibonacciValue;
    }
  }
}
5
Amarjit Datta

これは私が見つけた最高のビデオで、Javaでの再帰とフィボナッチ数列を完全に説明しています。

http://www.youtube.com/watch?v=dsmBRUCzS7k

これはシーケンスのための彼のコードであり、彼の説明は私が今までそれをタイプしようと試みることができるよりも優れています。

public static void main(String[] args)
{
    int index = 0;
    while (true)
    {
        System.out.println(fibonacci(index));
        index++;
    }
}
    public static long fibonacci (int i)
    {
        if (i == 0) return 0;
        if (i<= 2) return 1;

        long fibTerm = fibonacci(i - 1) + fibonacci(i - 2);
        return fibTerm;
    }
5
user2718538

Michael Goodrichらは、[fib(n)、fib(n-1)]の配列を返すことによって線形時間で再帰的にfibonacciを解くための、JavaのData Structures and Algorithmsで本当に賢いアルゴリズムを提供します。

public static long[] fibGood(int n) {
    if (n < = 1) {
        long[] answer = {n,0};
        return answer;
    } else {
        long[] tmp = fibGood(n-1);
        long[] answer = {tmp[0] + tmp[1], tmp[0]};
        return answer;
    }
}

これにより、fib(n)= fibGood(n)[0]が得られます。

4
Rae

フィボナッチ数列は、前の結果に1から始まる数を加算したものです。

      so.. 1 + 1 = 2
           2 + 3 = 5
           3 + 5 = 8
           5 + 8 = 13
           8 + 13 = 21

フィボナッチが何であるかを理解したら、コードを分割し始めることができます。

public int fibonacci(int n)  {
    if(n == 0)
        return 0;
    else if(n == 1)
      return 1;
   else
      return fibonacci(n - 1) + fibonacci(n - 2);
}

最初のif文は、ループが発生する可能性がある基本ケースをチェックします。その下のelse if文も同じことをしていますが、そのように書き直すことができます...

    public int fibonacci(int n)  {
        if(n < 2)
             return n;

        return fibonacci(n - 1) + fibonacci(n - 2);
    }

基本的なケースが確立されたので、呼び出しスタックを理解する必要があります。 "fibonacci"への最初の呼び出しは、呼び出された順序とは逆の順序で解決されるので、最後に解決されます。最後に呼び出されたメソッドが最初に解決され、最後に呼び出されたメソッドがその前に呼び出されます。

そのため、すべての呼び出しは、それらの結果を使用して「計算」される前に最初に行われます。入力が8の場合、出力は21になります(上の表を参照)。

fibonacci(n - 1)はベースケースに達するまで呼び出され続け、その後fibonacci(n - 2)はベースケースに達するまで呼び出されます。スタックが逆の順序で結果を合計し始めると、結果は以下のようになります...

1 + 1 = 1        ---- last call of the stack (hits a base case).
2 + 1 = 3        ---- Next level of the stack (resolving backwards).
2 + 3 = 5        ---- Next level of the stack (continuing to resolve).

正しい合計がスタック内の最初の呼び出しに返されるまで、それらはバブリングを続けます(後方への解決)。それがあなたの答えを得る方法です。

そうは言っても、このアルゴリズムは、コードが分割される各ブランチに対して同じ結果を計算するため、非常に非効率的です。もっと良い方法は、メモ化(キャッシング)や再帰(ディープコールスタック)が不要な「ボトムアップ」の方法です。

そのようです...

        static int BottomUpFib(int current)
        {
            if (current < 2) return current;

            int fib = 1;
            int last = 1;

            for (int i = 2; i < current; i++)
            {
                int temp = fib;
                fib += last;
                last = temp;
            }

            return fib;
        }
3

fibonacci シーケンスでは、最初の2つの項目は0と1で、他の各項目は前の2つの項目の合計です。すなわち:
0 1 1 2 3 5 8 ...

5番目の項目は4番目と3番目の項目の合計です。

3
yurib

この答えがなぜ違うのか

他のすべての答え:

  • 返品の代わりに印刷する
  • 反復ごとに2回の再帰呼び出しを行います。
  • ループを使用して質問を無視します

(さておき、これらのどれも実際には効率的ではありません。nを直接計算するために Binetの公式 を使用番目 期間)

尾部再帰ファイバー

これは、前の回答とその前の回答の両方を渡すことによって二重再帰呼び出しを回避する再帰的アプローチです。

private static final int FIB_0 = 0;
private static final int FIB_1 = 1;

private int calcFibonacci(final int target) {
    if (target == 0) { return FIB_0; }
    if (target == 1) { return FIB_1; }

    return calcFibonacci(target, 1, FIB_1, FIB_0);
}

private int calcFibonacci(final int target, final int previous, final int fibPrevious, final int fibPreviousMinusOne) {
    final int current = previous + 1;
    final int fibCurrent = fibPrevious + fibPreviousMinusOne;
    // If you want, print here / memoize for future calls

    if (target == current) { return fibCurrent; }

    return calcFibonacci(target, current, fibCurrent, fibPrevious);
}
2
AjahnCharles

ここで提供されるソリューションのほとんどは、O(2 ^ n)の複雑さで実行されます。再帰ツリーで同一ノードを再計算することは非効率的であり、CPUサイクルを浪費します。

メモ化を使用して、フィボナッチ関数をO(n)時間内に実行させることができます。

public static int fibonacci(int n) {
    return fibonacci(n, new int[n + 1]);
}

public static int fibonacci(int i, int[] memo) {

    if (i == 0 || i == 1) {
        return i;
    }

    if (memo[i] == 0) {
        memo[i] = fibonacci(i - 1, memo) + fibonacci(i - 2, memo);
    }
    return memo[i];
}

ボトムアップ動的計画法の経路に従うと、以下のコードはfibonacciを計算するのに十分単純です。

public static int fibonacci1(int n) {
    if (n == 0) {
        return n;
    } else if (n == 1) {
        return n;
    }
    final int[] memo = new int[n];

    memo[0] = 0;
    memo[1] = 1;

    for (int i = 2; i < n; i++) {
        memo[i] = memo[i - 1] + memo[i - 2];
    }
    return memo[n - 1] + memo[n - 2];
}
2
realPK

RanRag(承認済み)回答は問題なく機能しますが、Anil回答で説明されているように記憶されるまでは最適化された解決策ではありません。

以下のアプローチを再帰的に考慮すると、TestFibonacciのメソッド呼び出しは最小限です。

public class TestFibonacci {

    public static void main(String[] args) {

        int n = 10;

        if (n == 1) {
            System.out.println(1);

        } else if (n == 2) {
            System.out.println(1);
            System.out.println(1);
        } else {
            System.out.println(1);
            System.out.println(1);
            int currentNo = 3;
            calFibRec(n, 1, 1, currentNo);
        }

    }

    public static void calFibRec(int n, int secondLast, int last,
            int currentNo) {
        if (currentNo <= n) {

            int sum = secondLast + last;
            System.out.println(sum);
            calFibRec(n, last, sum, ++currentNo);
        }
    }

}
1
M Sach

理論的にはこの再帰的実装をマルチスレッド環境で適切に動作させることができる内部ConcurrentHashMapを使用することによって、BigIntegerとRecursionの両方を使用するfib関数を実装しました。最初の100個のfib番号を計算するのに約53msかかります。

private final Map<BigInteger,BigInteger> cacheBig  
    = new ConcurrentHashMap<>();
public BigInteger fibRecursiveBigCache(BigInteger n) {
    BigInteger a = cacheBig.computeIfAbsent(n, this::fibBigCache);
    return a;
}
public BigInteger fibBigCache(BigInteger n) {
    if ( n.compareTo(BigInteger.ONE ) <= 0 ){
        return n;
    } else if (cacheBig.containsKey(n)){
        return cacheBig.get(n);
    } else {
        return      
            fibBigCache(n.subtract(BigInteger.ONE))
            .add(fibBigCache(n.subtract(TWO)));
    }
}

テストコードは次のとおりです。

@Test
public void testFibRecursiveBigIntegerCache() {
    long start = System.currentTimeMillis();
    FibonacciSeries fib = new FibonacciSeries();
    IntStream.rangeClosed(0,100).forEach(p -&R {
        BigInteger n = BigInteger.valueOf(p);
        n = fib.fibRecursiveBigCache(n);
        System.out.println(String.format("fib of %d is %d", p,n));
    });
    long end = System.currentTimeMillis();
    System.out.println("elapsed:" + 
    (end - start) + "," + 
    ((end - start)/1000));
}
テストからの出力は
。
。
。
。
。
 fib of 93です。 94のfibは、19740274219868223167 
 95のfibは、31940434634990099905です。
 96のfibは、51680708854858323072です。 99のfibは218922995834555169026 
 100のfibは354224848179261915075 
経過:58,0 
1

これは簡単な方法だと思います。

public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int number = input.nextInt();
        long a = 0;
        long b = 1;
        for(int i = 1; i<number;i++){
            long c = a +b;
            a=b;
            b=c;
            System.out.println(c);
        }
    }
}
1
user3787713

これは、1 1 2 3 5 8 8の出力を表示または取得するのが基本シーケンスです。これは、前の数字と現在の数字の合計が次に表示されるシーケンスです。

Java Recursive Fibonacci sequence Tutorialの下のリンクを見てみてください

public static long getFibonacci(int number){
if(number<=1) return number;
else return getFibonacci(number-1) + getFibonacci(number-2);
}

ここをクリック スプーンフィーディングのためのJava再帰フィボナッチ数列チュートリアルを見る

1

以下は、1行のフェボナッチ再帰です。

public long fib( long n ) {
        return n <= 0 ? 0 : n == 1 ? 1 : fib( n - 1 ) + fib( n - 2 );
}
1
RonTLV

O(1)ソリューションは次のとおりです。

 private static long fibonacci(int n) {
    double pha = pow(1 + sqrt(5), n);
    double phb = pow(1 - sqrt(5), n);
    double div = pow(2, n) * sqrt(5);

    return (long) ((pha - phb) / div);
}

ビネのフィボナッチ数式 上記の実装に使用されます。大きな入力の場合、longBigDecimalに置き換えることができます。

0
Samir Agayarov

whileを使う:

public int fib(int index) {
    int tmp = 0, step1 = 0, step2 = 1, fibNumber = 0;
    while (tmp < index - 1) {
        fibNumber = step1 + step2;
        step1 = step2;
        step2 = fibNumber;
        tmp += 1;
    };
    return fibNumber;
}

このソリューションの利点は、コードが読みやすく理解しやすいことです。

0
Gavriel Cohen
public class febo 
{
 public static void main(String...a)
 {
  int x[]=new int[15];  
   x[0]=0;
   x[1]=1;
   for(int i=2;i<x.length;i++)
   {
      x[i]=x[i-1]+x[i-2];
   }
   for(int i=0;i<x.length;i++)
   {
      System.out.println(x[i]);
   }
 }
}
0
public class FibonacciSeries {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        for (int i = 0; i <= N; i++) {
            int result = fibonacciSeries(i);
            System.out.println(result);
        }
        scanner.close();
    }

    private static int fibonacciSeries(int n) {
        if (n < 0) {
            return 1;
        } else if (n > 0) {
            return fibonacciSeries(n - 1) + fibonacciSeries(n - 2);
        }
        return 0;
    }
}
0
user3231661

これを試して

private static int fibonacci(int n){
    if(n <= 1)
        return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

詳細については、これをチェックアウトする Javaで出力Fibonacciシリーズ - 普通のコード

0
markus rytter

フィボナッチ数列は、数の結果を合計したもので、前の結果に追加したものです。1から始める必要があります。前の番号と私は位置を変更しました。私は1から15までのフィボナッチ数列を検索しています。

public static void main(String args[]) {

    numbers(1,1,15);
}


public static int numbers(int a, int temp, int target)
{
    if(target <= a)
    {
        return a;
    }

    System.out.print(a + " ");

    a = temp + a;

    return numbers(temp,a,target);
}
0
Mathias Stavrou

補足するために、もっと大きな数を計算できるようにしたい場合は、BigIntegerを使用してください。

反復的な例です。

import Java.math.BigInteger;
class Fibonacci{
    public static void main(String args[]){
        int n=10000;
        BigInteger[] vec = new BigInteger[n];
        vec[0]=BigInteger.ZERO;
        vec[1]=BigInteger.ONE;
        // calculating
        for(int i = 2 ; i<n ; i++){
            vec[i]=vec[i-1].add(vec[i-2]);
        }
        // printing
        for(int i = vec.length-1 ; i>=0 ; i--){
            System.out.println(vec[i]);
            System.out.println("");
        }
    }
}
0
Tiago Zortea

http://en.wikipedia.org/wiki/Fibonacci_number 詳細

public class Fibonacci {

    public static long fib(int n) {
        if (n <= 1) return n;
        else return fib(n-1) + fib(n-2);
    }

    public static void main(String[] args) {
        int N = Integer.parseInt(args[0]);
        for (int i = 1; i <= N; i++)
            System.out.println(i + ": " + fib(i));
    }

}

Whileループや他のループを使用する必要がないように、必要なだけシンプルにする

0
vikas