演算法基礎:動態規劃陣列中滾動陣列的使用

淼叔發表於2020-11-01

這篇文章繼續在前一篇文章的基礎上介紹動態規劃陣列的優化方式。很多基礎演算法本來都是寫給我家的小少年看的,結果發現後浪學習的速度遠遠超出我的想象,在一個週末用這篇文章來紀念一下吧。


斐波那契數列

斐波那契數列:
f(n) = f(n-1) + f(n-2) (n>1)
f(0) = 1
f(1) = 1

dp陣列方式實現

int fibonacci(int n) {
    if (n == 0 || n == 1) return dp[n]=1;
    if (dp[n] != -1) return dp[n];
    return dp[n]=fibonacci(n-1)+fibonacci(n-2);
}

詳細可參看:https://liumiaocn.blog.csdn.net/article/details/109397755


計算次數的證明

自從發現少年已經自己會用spfa這樣的東西來解決問題之後,這種基礎的內容應該就不能發給他看了,所以簡單確認了一下動態規劃法到底解決了什麼問題,為什麼實際上加法的執行次數是fibonacci(n)-1,結果二十分鐘左右用歸納法就證明完了,除了結尾的fib(n)-1應該是fib(n+1)-1,基本似乎是對的。
在這裡插入圖片描述


動態規劃法解決

後浪看完給他寫的基礎教程,說這還可以用滾動陣列優化一下,給了個圖,使用Java方式1ms計算的斐波那契45的計算結果。
在這裡插入圖片描述


滾動陣列的使用

  • 第一種方式:使用&1
#define FIBONACCI_MAX 2
int dp[FIBONACCI_MAX] = { 0 };

int fibonacci(int n) {
    dp[0]=1,dp[1]=1;
    for (int i=2; i<=n; i++) dp[i&1] = dp[(i-1)&1] + dp[(i-2)&1];
    return dp[n&1];
}

程式碼說明:和1進行&的操作,只會有0和1兩個下標,因為此示例中使用的情況在求解的時候只是用到了n-1和n-2,這種大概是最節省的做法了,將本來長度為n的空間降到常量2.


  • 第二種方式:使用求餘
#define FIBONACCI_MAX 2
int dp[FIBONACCI_MAX] = { 0 };

int fibonacci(int n) {
    dp[0]=1,dp[1]=1;
    for (int i=2; i<=n; i++) dp[i%FIBONACCI_MAX] = dp[(i-1)%FIBONACCI_MAX] + dp[(i-2)%FIBONACCI_MAX];
    return dp[n%FIBONACCI_MAX];
}

程式碼說明:和第一中方式如出一轍,但是由於第一種和1進行&的情況只有兩種狀態,雖然可以解決大部分問題,但是如果有三個狀態需要使用的情況就會比較困難,可以考慮使用%,本質上是確認滾動時到底需要幾個元素進行運算。


總結

看到一個之前從來沒有接觸過計算機的少年在繁重的大學課程學習之餘1個月不到的時間從對程式設計一無所知到可以較為熟練使用Java和C進行滾動陣列/常見排序/鄰接矩陣/最短路徑等演算法進行實現,覺得很是欣慰和開心,真心感覺計算機演算法教育實際應該從初高中開始執行比較好。

相關文章