簡體   English   中英

手動計算遞歸Fibonacci算法的時間復雜度

[英]Manually calculating time complexity of recursive Fibonacci algorithm

我試圖理解遞歸Fibonacci算法的時間復雜度。

fib(n)
    if (n < 2)
        return n
    return fib(n-1)+fib(n-2)

由於沒有太多的數學背景,我嘗試手工計算它。 也就是說,當n增加時,我手動計算步數。 我忽略了我認為是恆定時間的所有事情。 我就是這樣做的。 說我想計算fib(5)。

n = 0 - just a comparison on an if statement. This is constant.
n = 1 - just a comparison on an if statement. This is constant.
n = 2 - ignoring anything else, this should be 2 steps, fib(1) takes 1 step and fib(0) takes 1 step.
n = 3 - 3 steps now, fib(2) takes two steps and fib(1) takes 1 step.
n = 4 - 5 steps now, fib(3) takes 3 steps and fib(2) takes 2 steps.
n = 5 - 8 steps now, fib(4) takes 5 steps and fib(3) takes 3 steps.

從這些方面來看,我認為運行時間可能是fib(n + 1)。 我不太確定1是否是常數因子,因為fib(n)和fib(n + 1)之間的差異可能非常大。

我在SICP上閱讀了以下內容

通常,樹遞歸過程所需的步驟數將與樹中的節點數成比例,而所需的空間將與樹的最大深度成比例。

在這種情況下,我相信樹中的節點數是fib(n + 1)。 所以我相信我是對的。 但是, 這段視頻讓我困惑:

所以這是一個時間復雜度實際上是順序的東西,結果是n的斐波那契。 有一種東西像Fibonacci數字一樣增長。

...

必須檢查此樹中的每個節點。

我絕對感到震驚。 我已經檢查了樹中的所有節點,並且總是有fib(n + 1)個節點,因此計算fib(n)時的步數。 我無法弄清楚為什么有些人說它是fib(n)步數而不是fib(n + 1)。

我究竟做錯了什么?

在您的程序中,您有這個耗時的操作(按每個操作使用的時間排序,在列表頂部快速操作):

  • 加成
  • IF(條件跳轉)
  • 從子程序返回
  • 函數調用

讓我們看一下這些動作的執行次數,並將其與n和fib(n)進行比較:

 n | fib | #ADD | #IF | #RET | #CALL  
---+-----+------+-----+------+-------
 0 |   0 |    0 |   1 |    1 |     0
 1 |   1 |    0 |   1 |    1 |     0

對於n≥2,您可以這樣計算數字:

  • fib(n)= fib(n-1)+ fib(n-2)
  • ADD(n)= 1 + ADD(n-1)+ ADD(n-2)
  • IF(n)= 1 + IF(n-1)+ IF(n-2)
  • RET(n)= 1 + RET(n-1)+ RET(n-2)
  • CALL(n)= 2 + CALL(n-1)+ CALL(n-2)

為什么?

  • ADD:一個加法直接在程序的頂層實例中執行,但在這兩個子程序中,您調用的也是需要執行的附加項。
  • IF和RET:與以前相同的參數。
  • CALL:也是一樣,但你在頂層實例中執行兩次調用。

所以,這是你的其他n值列表:

  n |    fib |   #ADD |    #IF |   #RET |  #CALL  
 ---+--------+--------+--------+--------+--------
  0 |      0 |      0 |      1 |      1 |      0
  1 |      1 |      0 |      1 |      1 |      0
  2 |      1 |      1 |      3 |      3 |      2
  3 |      2 |      2 |      5 |      5 |      4
  4 |      3 |      4 |      9 |      9 |      8
  5 |      5 |      7 |     15 |     15 |     14
  6 |      8 |     12 |     25 |     25 |     24
  7 |     13 |     20 |     41 |     41 |     40
  8 |     21 |     33 |     67 |     67 |     66
  9 |     34 |     54 |    109 |    109 |    108
 10 |     55 |     88 |    177 |    177 |    176
 11 |     89 |    143 |    287 |    287 |    286
 12 |    144 |    232 |    465 |    465 |    464
 13 |    233 |    376 |    753 |    753 |    752
 14 |    377 |    609 |   1219 |   1219 |   1218
 15 |    610 |    986 |   1973 |   1973 |   1972
 16 |    987 |   1596 |   3193 |   3193 |   3192
 17 |   1597 |   2583 |   5167 |   5167 |   5166
 18 |   2584 |   4180 |   8361 |   8361 |   8360
 19 |   4181 |   6764 |  13529 |  13529 |  13528
 20 |   6765 |  10945 |  21891 |  21891 |  21890
 21 |  10946 |  17710 |  35421 |  35421 |  35420
 22 |  17711 |  28656 |  57313 |  57313 |  57312
 23 |  28657 |  46367 |  92735 |  92735 |  92734
 24 |  46368 |  75024 | 150049 | 150049 | 150048
 25 |  75025 | 121392 | 242785 | 242785 | 242784
 26 | 121393 | 196417 | 392835 | 392835 | 392834
 27 | 196418 | 317810 | 635621 | 635621 | 635620

您可以看到,添加的數量恰好是函數調用數量的一半(好吧,您也可以直接從代碼中讀取它)。 如果將初始程序調用計為第一個函數調用,那么您具有完全相同的IF,返回和調用量。

因此,您可以將1個ADD,2個IF,2個RET和2個CALL組合到一個需要恆定時間的超級動作中。

您還可以從列表中讀取,添加數量比fib(n + 1)少1(可以忽略)。

因此,運行時間是有序的fib(n+1)

fib(n+1) / fib(n)的比率越接近Φ越大,n越大。 Φ是黃金比例,即1.6180338997是常數。 並且在訂單中忽略常數因子。 這樣,順序O(fib(n+1))是完全一樣O(fib(n))


現在讓我們來看看空間:

確實,處理樹所需的最大空間等於樹與最大遠葉之間的最大距離。 這是事實,因為你在f(n-1)返回后調用f(n-2)。

所以你的程序所需的空間是n階。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM