遞歸和循環計算斐波那契數列第n項的值

2021-01-22 簡單說架構

遞歸和循環計算斐波那契數列第n項的值。遞歸的底層實現原理。

開始

計算斐波那契數列第n項的值。

斐波那契數列

斐波那契數列,是數學家以兔子繁殖為例子引入,又稱為兔子數列。如下:

1、1、2、3、5、8、12、21、34…

第一項和第二項為1,其後,每一項都是它之前兩項的和。歸納公式如下:

f(1) = 1

f(2) = 2

f(n) = f(n-1) + f(n-2) (n>=3,n∈N*)

算法

我們可以通過遞歸的方式,得到結果,如下:

遞歸

遞歸是一個程序自身調用自身的一個過程,這句話,聽起來說起來簡單,理解起來又有些複雜。那應該怎麼去理解它呢?我們從兩個層面去理解:

1. 自己調用自己。在一個方法的內部調用這個方法本身,這是遞歸的基本結構。有這個結構,才叫遞歸。

2. 有終止條件。這個其實不算是遞歸的結構,但卻是一個正確的遞歸方法的必要條件。因為,如果沒有終止條件,那麼,自己將不斷調用自己,好像陷入了一個無底洞一般。(注意,這是不是死循環)。所以,一個正確的遞歸方法,必然需要一個有效的終止條件。

計算階乘的遞歸

棧是一種線性表數據結構,一端為棧頂,另一端為棧底,所有元素只能從棧頂進入,稱為入棧,也只能從棧頂取出,稱為出棧。

從JVM內存角度來看,棧是一塊特殊的內存空間,這塊內存空間,之所以稱之為棧,就是因為,它和數據結構棧有著相似的操作方式。

每開啟一個線程,JVM就會為它分配一個棧,棧中,以幀為單位,保存線程的運行狀態,JVM對棧的操作只有兩種,入棧一個幀,或者出棧一個幀。

幀用來保存線程的運行狀態,那麼,線程的運行狀態指什麼?我們說,線程的的運行狀態,指的是當前線程正在執行的方法,以及調用這個方法時傳入的參數,方法的局部變量和中間運行結果。正在執行的方法,這個不用解釋。調用方法時傳入的參數,因為同一個方法,參數不同,結果自然不同,所以,這肯定是一個運行狀態。局部變量,方法中的每個局部變量都和方法的最終結果相關,所以,這也算一個運行狀態。中間運行結果,當前方法執行到了哪一步,某個表達式的運算結果是什麼,也這和方法的結果相關。

綜上,我們發現,幀,對應了一個方法。事實上,JVM也是這麼做的,每開始調用一個方法,就會入棧一個幀,每執行完成一個方法,就會出棧一個幀。

假設某線程中,方法A調用B,B又調用了C。那麼,JVM對棧的操作可能如下:

幀(A)入棧

幀(B)入棧

幀(C)入棧

幀(C)出棧

幀(B)出棧

幀(A)出棧

假設某線程中,包含A方法的遞歸操作,那麼,JVM對棧的操作可能如下:

幀(A1)入棧

幀(A2)入棧

幀(A3)入棧

………………

幀(A3)出棧

幀(A2)出棧

幀(A1)出棧

A後面不同的數字,代表它們是同一個方法,不同的幀。

所以,我們發現,遞歸,事實上是同一個方法,不斷的入棧,等到達終止條件後,再不斷的出棧的過程。假設有一段臺階,從下至上,每層臺階上都放著一個蘋果,已知每層臺階的蘋果是否有毒,都和上一層臺階的蘋果結論相反,且至少有某一層,蘋果上有明確的標識說明是否有毒。那麼,該如果得知每一層的蘋果是否有毒呢?你只有一層一層向上,找到有明確標識的那一層,再一層一層向下,計算得到第一層蘋果是否有毒。遞歸,就是這樣一個拾階而上,再逐階而下的過程。

這個例子中,如果沒有任何一層的蘋果有明確是否有毒,那麼,你就會一直向上,而得不到結果。也就是說,如果遞歸沒有有效的終止條件,就會一直入棧,而無法出棧。我們知道,棧是一塊內存空間,自然有大小限制。不斷的入棧,就會導致棧深越界的錯誤發生。

循環

滿足特定條件時,不斷重複某一操作(循環體),即為循環。

對於JVM來說,循環是語句跳轉的一種,JVM通過一塊叫做程序計數器的內存空間,來存儲當前執行的語句的位置,並通過修改這個值,來達到語句跳轉的目的。

也就是說,循環是沒有不斷入棧和不斷出棧這個過程的,它只是在進入這個方法時,入棧一次,方法運行結束時,出棧一次,所以,一般來說,循環都比遞歸的效率要高,而且,也不會佔用大量的棧內存空間。

遞歸算法,一般也可以寫成循環。

如拾蘋果的例子,你就可以在上臺階的過程中,記住上了臺階的數量,在得到有標記的蘋果時,通過計算,得到第一層蘋果是否有毒。

再比如說斐波那契數列的循環實現方式如下:

結論

能用循環解決的問題,就不要用遞歸。

相關焦點

  • 求職乾貨:再也不怕面試官問斐波那契數列了! - CSDN
    斐波那契數列的計算表達式很簡單:F(n) = n; n = 0,1F(n) = F(n-1) + F(n-2),n >= 2;因此,我們能很快根據表達式寫出遞歸版的代碼:/*fibo.c*/#include<stdio.h>#include<stdlib.h>/*求斐波那契數列遞歸版*/unsignedlongfibo
  • R語言中repeat循環的使用方法之素數和斐波那契數列前n項
    R語言提供了三種循環方法:for循環、while循環和repeat循環。for循環和while循環在本號前面兩篇文章已經介紹過了,這篇文章將詳細介紹repeat循環的使用方法。#求出100以內的所有素數n <- 2 #從2開始判斷repeat #循環要判斷的數字{if(n >= 100) #如果超過100,則跳出break;j <- 2 #用2~n的數j去除nrepeat #循環2~n{if(n %% j == 0
  • 斐波那契數列的兩個令人著迷的特性 - 電子通信和數學
    斐波那契數列是一個眾所周知的且經過研究的數字序列,經常在學校和休閒數學中使用,因為它很容易被那些受過有限的專業數學教育的人理解。序列的定義如下:第一項是零,第二項是一,任何其他項都是序列前兩項的和。這個序列的正式寫法如下當n> 1時。
  • Fibonacci 斐波那契數列的幾種寫法、時間複雜度對比
    斐波那契數列(Fibonacci)最早由印度數學家Gopala提出,第一個真正研究斐波那契數列的是義大利數學家 Leonardo Fibonacci,斐波那契數列的定義很簡單,用數學函數可表示為:用 Python 實現斐波那契數列常見的寫法有三種,各算法的執行效率也有很大差別,在面試中也會偶爾會被問到,通常面試的時候不是讓你簡單的用遞歸寫寫就完了,還會問你時間複雜度怎樣,空間複雜度怎樣,有沒有可改進的地方。
  • 斐波那契數列
    有148位數,你知道它是第幾個斐波那契數嗎?今天就要解答這個問題,上面這個數是第708個斐波那契數。斐波那契數列經常被用來做為寫遞歸的例子,但遞歸算起來實在是慢,因為有太多重複計算在裡面。這裡依然是使用結構體,方便存儲相關的信息,這裡記錄了緩存向量最大有多大,實際存儲有多大,以及緩存的斐波那契數列。題目要求是給我們好多行數字,第一行是後面數字的個數,然後我們讀了數字要報出來是第幾個斐波那契數。
  • python實戰14遞歸算法實現斐波那契數列(BAT面試題)
    斐波那契數列簡介斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因數學家萊昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖為例子而引入,故又稱為「兔子數列」,指的是這樣一個數列:0、1、1、2、3
  • 雲計算開發學習實例:Python3 斐波那契數列
    原標題:雲計算開發學習實例:Python3 斐波那契數列   斐波那契數列指的是這樣一個數列 0, 1, 1, 2, 3, 5, 8, 13,特別指出:第0項是
  • Python 揭秘斐波那契定律,如何幫助碼農分析股票?|技術頭條
    在數學上,我們可以用遞歸的方法來定義斐波納契數列的生成規律,如下所示:當n=0時,F(n)=0當n=1時,F(n)=1當n>1時,F(n)= F(n-1)+ F(n-2)(3) 斐波那契數列神奇之處在哪?斐波那契數列存在許多神奇的性質,我們不妨對斐波那契數列中相鄰的兩個數求商值。
  • 斐波那契數列為什麼那麼重要,所有關於數學的書幾乎都會提到?
    下面我就盡我所能,講述一下斐波那契數列。一、起源和定義斐波那契數列最早被提出是印度數學家Gopala,他在研究箱子包裝物件長度恰好為1和2時的方法數時首先描述了這個數列。也就是這個問題:有n個臺階,你每次只能跨一階或兩階,上樓有幾種方法?
  • Python 揭秘斐波那契定律,如何幫助碼農分析股票?| 技術頭條
    在數學上,我們可以用遞歸的方法來定義斐波納契數列的生成規律,如下所示:(3) 斐波那契數列神奇之處在哪?斐波那契數列存在許多神奇的性質,我們不妨對斐波那契數列中相鄰的兩個數求商值。        return Fibonacci_Generate(n-1)+ Fibonacci_Generate(n-2)分別繪製出包含10項和30項數值的斐波那契數列增長曲線,如下圖所示。
  • 小學生也可以寫出的,複雜數列-斐波那契數列
    數列前兩項之和等於第三項如果設F(n)為該數列的第n項(n∈N*),那麼這句話可以寫成如下形式::F(n)=F(n-1)+F(n-2)以上就是斐波那契數列的官方定義。其實只要把數列擺出來,我們就可以很明確的看到特性了。1,1,2,3,5,8,13,21,34...
  • 最美的數列-斐波那契數列
    義大利數學家斐波那契斐波那契,也叫做比薩的李奧納多(1175年-1250年),中世紀義大利數學家,是西方第一個研究斐波那契數的人,並將現代書寫數和乘數的位值表示法系統引入歐洲。其寫於1202年的著作《計算之書》中包涵了許多希臘、埃及、阿拉伯、印度、甚至是中國數學相關內容。斐波那契數列, 就是由這位義大利著名數學家萊昂納多·斐波那契在《計算之書》中以兔子繁殖為例子而提出的數列,故又稱為「兔子數列」。斐波那契數列:1、1、2、3、5、8、13、21、34、56……這個數列的特點是從第3項開始,每一項都是前兩項的和。
  • python邏輯控制總結——斐波那契數列
    控制臺更多的PyCharm的工具的使用,同學們可以自行百度和熟悉。斐波那契數列斐波那契數列在數學上,是一個特殊的數列,它的特徵如下:1. 第一項和第二項均為1。斐波那契數列代碼1運行結果如下:核心邏輯是根據while後面的判斷條件進行判斷,判斷為True,則不斷重複執行循環體中的內容,直接判斷為False或手動跳出循環。如在計算斐波那契數列時,要求輸出前10項,我們就定義了一個變量 i,初始值為1。while進入的條件為i <= 10,在循環體中,有一個對 i 的修改,即 i += 1,每次都加1。
  • Scratch3.0編程小課堂42(神奇的斐波那契曲線)
    ,那麼每月兔子的總數可以用以下數列表示:1,1,2,3,5,8,13,21,34,55,89,144,233…這一數列看起來相當簡單,但卻隱藏著一些有趣的東西,如果從第0項開始,它的值是0 ,第1項是1,……那麼數列中後面的每一項都等於前兩項之和,稱為斐波那契數列(Fibonacci sequence)。
  • 生命曲線與神聖的斐波那契數列
    同樣讓他感到驚訝的是,最讓世人津津樂道是以他命名的這個斐波那契數列:0,1,1,2,3,5,8,13……,而並非本人更偉大的數學成就——將阿拉伯數字和乘數的位值表示法系統引入了歐洲。斐波那契為這些商人編寫了《計算之書》,其中就涉及到大量的實際問題,並舉例說明,與笨拙的羅馬數字相比,這套新的數字系統可以多麼簡單、高效地進行商業和數學計算。透過斐波那契的這本書將十進位數字影響傳播開來是他最偉大的數學成就。然而,本人卻是因為《計算之書》中列舉的斐波那契數列被世人所熟知的。
  • 利用斐波那契數列實現英裡和公裡轉換
    這個有趣的數學 trick 源於一個實證觀察和斐波那契數列。首先,我們定義英裡和公裡的關係:1英裡 = 1.60934公裡,1公裡 = 0.621371英裡如果看起來很熟悉,那是因為這些數字非常接近黃金分割率。
  • 斐波那契數列有多神奇?
    =上一年的兔子對數+上一年的大兔子對數=上一年的兔子對數+上上一年的兔子對數也就是說,如果把第 n 年的兔子對數記為 Fn,則:由於這個數列是由 斐波那契 首先加以研究的,後人就將其稱為 斐波那契數列。
  • 她研究的斐波那契數列到底是啥?
    她在初中階段就憑藉課題「斐波那契數列與貝祖數的估計」獲得了「第33屆全國青少年科技創新比賽」一等獎、專項獎一項。實際上,「斐波那契數列」指這樣一個數列: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377,…… 數列中的每1項稱為「斐波那契數」,從第3項開始,每1項都等於前2項之和。
  • 非常奇妙:黃金分割率、斐波那契數列、楊輝三角與易經河洛的關係
    21、34、……在數學上,斐波納契數列以如下被以遞歸的方法定義:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)在現代物理、準晶體結構、化學等領域,斐波納契數列都有直接的應用,為此,美國數學會從1963起出版了以《斐波納契數列季刊》為名的一份數學雜誌,用於專門刊載這方面的研究成果。
  • python迭代器和生成器總結——新的斐波那契數列
    前面一篇文章裡我們聊到了斐波那契數列的demo,當時一個用for循環實現的代碼如下:斐波那契數列1demo裡,我們在for語句塊的上面,定義了三個變量,分別是lastOne,lastTwo和current,這樣做的原因是為了保存每次循環後,lastOne和lastTwo的結果。