Fibonacci數列問題
Fibonacci數列的解法:
1、遞歸算法
遞歸的概念,我說不清楚,語文不好。但是核心思想,我認為就是入棧出棧。比方說,你想要求得某個結果,如果一步求解不出來,那麽先把最後一步的計算步驟進棧,先不考慮 它。轉而去想,在求解最後一步之前的那一步應該怎麽去做,就好比冬天穿衣服,再最後一步穿羽絨服之前我想大部分人會先穿一個羊毛衫(要是較真的話,內衣你應該會穿的吧)。這樣,我們先把羽絨服放在一邊(進棧),先去準備穿羊毛衫。然後,我們又發現在穿羊毛衫之前,我得穿個保暖內衣啊。好,我們再將羊毛衫先放一邊(再進棧,此時羊毛衫在羽絨服上面放著)。這時候,我們穿上了保暖內衣,ok一下個——>棧的後入先出規則表示下一個我應該穿羊毛衫,ok穿上再下一個——>羽絨服。。。(下半身的穿衣流程類似,可能多一個胖次,自行腦補思考)。
所以你看,現實當中很多時候的事情不是一下就可以完成的,而是需要一步一步的去完成,程序更是如此。你設計的代碼,目的就是為了解決一個問題,在解決的過程中,會要進行很多的步驟,遞歸也是這樣,只是它在解決問題之前一直在調用自身。
來看一下這個Fibonacci數列:
Fibonacci數列的遞推公式為:Fn=Fn-1+Fn-2,其中F1=F2=1。
當n比較大時,Fn也非常大,現在我們想知道,Fn除以10007的余數是多少。
第一步,n是多少。按照題目意思,自己輸入n。
第二步,怎麽求,這裏我假設有這麽一個方法就叫做Fibonacci(int n),return一個int,將n傳進去之後,經過這個方法後,我就得到了Fn。
第三步,分析一下,Fn=Fn-1+Fn-2=Fn-2+Fn-3+Fn-3+Fn-4=..........所以,要求Fn(羽絨服),先去求Fn-1和Fn-2(羊毛衫),要求Fn-1就要先去求Fn-2和Fn-3,以此類推,暫時 求不出來的,統統依次入棧,最後發現要求F3,我得先求F2和F1(F3入棧)。OK,F1=F2=1,F3就求出來了(F3出棧),又F4=F3+F2,F4也求得出來了(F4出棧).......之前入 棧的那些步驟,再依次出棧進行計算,直到Fn被求出來,計算Fn%10007本題結束。
第四步,放代碼:
import java.util.Scanner; public class Fibonacci { //Fibonacci數列 遞歸算法 public static int Fibonacci (int n){ if(n==1||n==2){ return 1; } else{ //操作入棧出棧的核心,遞歸調用 n = Fibonacci (n-1)+Fibonacci (n-2); return n; } } public static void main(String[] args) { while(true){ Scanner scanner = new Scanner(System.in); int n; n = scanner.nextInt(); int k = Fibonacci (n); System.out.println(k%10007); } } }
2、整數求余
遞歸的算法雖然代碼簡潔,但是有一個比較不太好的地方,就是執行的效率很低,它是自身層層深入的調用,在時間和空間上的復雜度都很高,而且計算的重復性操作很多,在計算機內部還很有可能造成棧溢出的現象。比方說,用遞歸的方法計算N階計算的時候,當N的值越來越大,很可能計算機就罷工了。
所以,本著遞歸和循環理論相同的原則,結合整數求余的規律,將代碼改進如下:
import java.util.Scanner; public class Fibonacci { //Fibonacci數列 求余算法 public static int Fibonacci(int n){ int[] F = new int[n+1];//n+1是為了不讓數組出現越界 F[1]=F[2]=1; for(int i=3;i<=n;i++){ F[i] = (F[i-1] + F[i-2])%10007; } return F[n]; } public static void main(String[] args) { while(true){ Scanner scanner = new Scanner(System.in); int n; n = scanner.nextInt(); System.out.println(Fibonacci(n)); } } }
在方法裏面定義一個長度為n+1的int型數組,用來存儲數列的值,其中n+1是為了防止數組在執行過程當中產生越界的現象。然後,方法裏面的核心在於整數求余的計算,即:
F[i] = (F[i-1] + F[i-2])%10007;
為什麽這樣寫?當代碼改進成這樣時,我們就不需要先求得Fn的值再去取余數,而是直接在for循環中,直接就能將取余的運算執行,當循環結束。最後得到的Fn,就是數列中第N個數取余的結果。
為什麽可以這樣寫?這裏面是有整數取余運算的規律的,舉個例子,1%10取余數就是1,2%10就是2,3%10就是3,那是不是3%10=1%10+2%10?再多舉幾個例子來進行驗證:
5%10=5;10%10=0;15%10=5=5%10+10%10;
21%10=1;32%10=2;53%10=3=21%10+32%10;
57%7=1;89%7=5;146%7=6=57%7+89%7;
......
可以看到,好像的確是這樣,這當中的原理,其實你思考一下是可以理解的,但是我說不太清楚,害怕誤人子弟,想要深入理解,可以百度。
附加我找的一位前輩的博客鏈接,記錄了取余運算規則:https://blog.csdn.net/ash_zheng/article/details/38541777
暫時就寫到這了,歡迎評論交流,互相學習,哈哈。
Fibonacci數列問題