算法 遞歸 迭代 動態規劃 斐波那契數列 MD

Markdown版本筆記 個人GitHub首頁 個人博客 個人微信 個人郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

算法 遞歸 迭代 動態規劃 斐波那契數列 MDgit


目錄

遞歸和迭代

什麼是遞歸

遞歸的基本概念:程序調用自身的編程技巧稱爲遞歸github

一個函數在其定義中直接或間接調用自身的一種方法,它一般把一個大型的複雜的問題轉化爲一個與原問題類似的規模較小的問題來解決,能夠極大的減小代碼量。算法

遞歸的能力在於用有限的語句來定義對象的無限集合.編程

因爲遞歸引發一系列的函數調用,而且有可能會有一系列的重複計算,遞歸算法的執行效率相對較低。數組

遞歸的優劣
優勢微信

  • 大問題化爲小問題,能夠極大的減小代碼量
  • 用有限的語句來定義對象的無限集合
  • 代碼更簡潔清晰,可讀性更好

缺點函數

  • 遞歸容易產生"棧溢出"錯誤(stack overflow)。由於須要同時保存成百上千個調用記錄,因此遞歸很是耗費內存。
  • 遞歸可能存在冗餘計算。好比最典型斐波那契數列,計算第6個須要計算第4個和第5個;而計算第5個還須要計算第4個,所處會重複。迭代在這方面有絕對優點。

什麼是迭代法

迭代法也稱展轉法,是一種不斷用變量的舊值遞推新值的過程,跟迭代法相對應的是直接法,即一次性解決問題。學習

迭代法是一類利用遞推公式循環算法經過構造序列來求問題近似解的方法。優化

迭代算法是用計算機解決問題的一種基本方法,它利用計算機運算速度快、適合作重複性操做的特色,讓計算機對一組指令(或必定步驟)進行重複執行,在每次執行這組指令(或這些步驟)時,都從變量的原值推出它的一個新值,迭代法又分爲精確迭代和近似迭代。比較典型的迭代法如二分法牛頓迭代法屬於近似迭代法。spa

遞歸和迭代的區別

知乎 上有不少人舉了很是生動的例子來講明他們的區別,下面摘錄一些比較經典的描述。

遞歸就是本身調用本身,本身包含本身。
迭代是將輸出作爲輸入,再次進行處理。

遞歸過程當中, 問題的規模在縮小,這樣最終獲得問題的解;
迭代是一種由遠變近的逼近,問題的規模不見得縮小了,可是慢慢在調整接近答案。

遞歸——《盜夢空間》,不斷下潛至底層並最終解決
迭代——《明日邊緣》,不斷回到同一個場景並優化解決

遞歸是一個樹結構,每一個分支都探究到最遠,發現沒法繼續的時候往回走;
迭代是一個環結構,每次迭代都是一個圈,不會拉掉其中的某一步,而後不斷循環;

迭代是更新變量的舊值
遞歸是在內部調用自身

迭代是循環結構,例如 for while 循環
遞歸是選擇結構,例如 if else 調用本身

在數學上,遞歸強調的是,新的值與前面計算的好幾個值有關係 F{n} =F{n-1} +F{n-2}
而迭代通常是隻是 x{n+1} 與 x{n} 之間進行計算

迭代是逐漸逼近,用新值覆蓋舊值,直到知足條件後結束,不保存中間值,空間利用率高。
遞歸是將一個問題分解爲若干相對小一點的問題,遇到遞歸出口再原路返回,所以必須保存相關的中間值,這些中間值壓入棧保存,問題規模較大時會佔用大量內存。

動態規劃

動態規劃 Dynamic Programming,簡稱DP。經過把原問題分解爲相對簡單的子問題的方式求解複雜問題的方法。

動態規劃程序設計是對解最優化問題的一種途徑、一種方法,而不是一種特殊算法。不像搜索或數值計算那樣,具備一個標準的數學表達式和明確清晰的解題方法。

動態規劃程序設計每每是針對一種最優化問題,因爲各類問題的性質不一樣,肯定最優解的條件也互不相同,於是動態規劃的設計方法對不一樣的問題,有各具特點的解題方法,而不存在一種萬能的動態規劃算法,能夠解決各種最優化問題。所以讀者在學習時,除了要對基本概念和方法正確理解外,必須具體問題具體分析處理,以豐富的想象力去創建模型,用創造性的技巧去求解。咱們也能夠經過對若干有表明性的問題的動態規劃算法進行分析、討論,逐漸學會並掌握這一設計方法。

基本思想

動態規劃算法一般用於求解具備某種最優性質的問題。在這類問題中,可能會有許多可行解。每個解都對應於一個值,咱們但願找到具備最優值的解。

動態規劃算法與分治法相似,其基本思想也是將待求解問題分解成若干個子問題,先求解子問題,而後從這些子問題的解獲得原問題的解。

與分治法不一樣的是,適合於用動態規劃求解的問題,經分解獲得子問題每每不是互相獨立的。若用分治法來解這類問題,則分解獲得的子問題數目太多,有些子問題被重複計算了不少次。若是咱們可以保存已解決的子問題的答案,而在須要時再找出已求得的答案,這樣就能夠避免大量的重複計算,節省時間。咱們能夠用一個表來記錄全部已解的子問題的答案。無論該子問題之後是否被用到,只要它被計算過,就將其結果填入表中。這就是動態規劃法的基本思路。

具體的動態規劃算法多種多樣,但它們具備相同的填表格式。

關鍵字:分解成若干個子問題,保存子問題的解,從子問題的解獲得原問題的解

問題特徵
動態規劃經常適用於有最優子結構和重疊子問題性質的問題:

  • 最優子結構:當問題的最優解包含了其子問題的最優解時,稱該問題具備最優子結構性質。
  • 重疊子問題:在用遞歸算法自頂向下解問題時,每次產生的子問題並不老是新問題,有些子問題被反覆計算屢次。動態規劃算法正是利用了這種子問題的重疊性質,對每個子問題只解一次,然後將其解保存在一個表格中,在之後儘量經過查表以利用這些子問題的解。

適用條件

適用動態規劃的問題必須知足最優化原理無後效性

最優化原理(最優子結構性質)
最優化原理可這樣闡述:一個最優化策略具備這樣的性質,不論過去狀態和決策如何,對前面的決策所造成的狀態而言,餘下的諸決策必須構成最優策略。簡而言之,一個最優化策略的子策略老是最優的。一個問題知足最優化原理又稱其具備最優子結構性質。

我的理解:例如求最短路徑問題,從起點到終點的最短路徑,必定也是這條路徑上任意一點到終點的最短路徑。

無後效性
將各階段按照必定的次序排列好以後,對於某個給定的階段狀態,它之前各階段的狀態沒法直接影響它將來的決策,而只能經過當前的這個狀態。換句話說,每一個狀態都是過去歷史的一個完整總結。這就是無後向性,又稱爲無後效性。

我的理解:例如求最短路徑問題,前 N 步的路徑(所謂的之前階段的狀態)並不會對後續最優路徑的選擇產生影響,惟一對後續最優路徑的選擇產生影響的是當前所處的位置(所謂的狀態)

子問題的重疊性
動態規劃將原來具備指數級時間複雜度的搜索算法改進成了具備多項式時間複雜度(包括O(lgn)、O(n)、O(n^2)、O(n^3)等)的算法。其中的關鍵在於解決冗餘,這是動態規劃算法的根本目的。動態規劃實質上是一種以空間換時間的技術,它在實現的過程當中,不得不存儲產生過程當中的各類狀態,因此它的空間複雜度要大於其它的算法。

斐波那契數列

1,1,2,3,5,8,13,21,34,55

耗時時間比較:

n 遞歸法 迭代法 動態規劃
20 0-2 所有不超過 1 所有不超過 1
25 1-4 0 0
30 5-8 0 0
35 50 0 0
40 489 0 0
41 803 0 0
42 1323 0 0
43 2025 0 0
44 3293 0 0
45 5309 0 0

遞歸法實現

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

迭代法實現

方式1:

public static int iterFib(int n) {
    if (n < 2) {
        return n;
    } else {
        int result = 0, a = 0, b = 1;
        for (int i = 2; i <= n; i++) {
            result = a + b; //每次都是最近的兩個值的和
            a = b;// 把最舊的值替換爲第二舊的值
            b = result; //把第二舊的值替換爲最新的值
        }
        return result;
    }
}

方式2:

public static int iterFib(int n) {
    if (n < 2) {
        return n;
    } else {
        int result = 0, a = 0, b = 1;
        for (int i = 2; i <= n; i++) {
            result = a + b; //每次都是最近的兩個值的和
            if (a < b) a = result; //較小的值是更舊的值,咱們把最新的值替換爲更舊的值,另外一個值保持不變
            else b = result;
        }
        return result;
    }
}

動態規劃實現

public static int dynFib(int n) {
    int[] val = new int[n + 1];
    if (n < 2) {
        return n;
    } else {
        val[1] = 1;
        for (int i = 2; i <= n; i++) {
            val[i] = val[i - 1] + val[i - 2];
        }
        return val[n];
    }
}

在使用動態規劃實現時,咱們用數組保留中間值,這些中間值便是動態規劃定義中的小問題

但須要注意的是,用動態規劃解決斐波那契數列時能夠不用數組,由於在計算某個位置上的數時只須要用到前兩位的值,因此咱們只須要動態的保留前兩位的值便可。這樣子的動態規劃的實現就和迭代是同樣的了,但在其餘問題上多是不同的。

2018-12-9

相關文章
相關標籤/搜索