細談遞迴,備忘錄遞迴,動態規劃,三種演算法思想和執行原理
阿新 • • 發佈:2019-01-05
大家都知道,數值稍大的遞迴執行時間對於開發者來說就是場災難,我們總是想方設法在優化遞迴,或者說不用遞迴,此文中從空間時間角度詳細剖析以上三種演算法的區別,以及執行原理,以斐波那契數為例, 程式語言java
此處為程式碼
package test1;
import java.util.HashMap;
public class test2 {
private static int count1=0;
private static int count2=0;
private static int count3=0;
public static void main(String[] args) {
System.out.println("***********************遞迴*********************");
long startTime1=System.nanoTime();
System.out.println(getNumber1(30));
long endTime1=System.nanoTime();
System.out.println("方法呼叫了"+count1+"次數");
System.out.println("時間為:" +(endTime1-startTime1)+"ns");
System.out.println("***********************備忘錄遞迴*********************");
long startTime2=System.nanoTime();
System.out.println(getNumber2(30));
long endTime2=System.nanoTime();
System.out.println("方法呼叫了"+count2+"次數");
System.out .println("時間為:"+(endTime2-startTime2)+"ns");
System.out.println("***********************動態規劃*********************");
long startTime3=System.nanoTime();
System.out.println(getNumber3(30));
long endTime3=System.nanoTime();
System.out.println("方法呼叫了"+count3+"次數");
System.out.println("時間為:"+(endTime3-startTime3)+"ns");
}
//遞迴
// 832040
// 方法呼叫了1664079次數
// 時間為:4839154ns
public static int getNumber1( int m) {
count1++;
if(m==1||m==2)
{
return 1;
}
else
return getNumber1(m-1)+getNumber1(m-2);
}
// 備忘錄演算法,自上而下,記住之前算過的值。減少方法的訪問次數從而減少執行時間
//方法呼叫了:57
//832040
//時間為:133048ns
private static HashMap<Integer, Integer> hm=new HashMap<>();
public static int getNumber2( int m) {
count2++;
if(m==1||m==2)
{
return 1;
}
else if(hm.containsKey(m))
{
return hm.get(m);
}
else {
int value =getNumber2(m-1)+getNumber2(m-2);
hm.put(m, value);
return value;
}
}
// 動態規劃,自下向上的演算法
//方法呼叫了:1
//時間為:3422ns
//832040
public static int getNumber3( int m) {
count3++;
if(m==1||m==2)
{
return 1;
}
int a=1;
int b=1;
int temp=0;
for(int x=3;x<=m;x++)
{
temp=a+b;
a=b;
b=temp;
}
return temp;
}
}
先來說說第一種遞迴這是一種最常見的遞迴 也是最好理解的,在這裡我就不再贅述,原理和執行方式,只是提一句,這種遞迴是一種自上向下的遞推過程。而它的執行時間,以及呼叫次數如下。
**這是當引數m為4的時候,由於此方法中只有一個m變數,在記憶體中執行的時候便不會佔用時間和空間,這是比備忘錄遞迴好的地方,當然這也僅僅侷限於當引數m小的時候,而隨著m的增大直到30,這時候顯而易見備忘錄遞迴的優勢就會體現出來,這時候在方法呼叫和執行時間上都有明顯的提升,如果說硬要有遜色的話,也就多佔了一個hashmap記憶體空間,不過這在8GB的執行記憶體中顯然不算什麼。**
其次,再來說說備忘錄遞迴的執行原理**
這是我在圖片編輯器中以引數m=4的時候為例畫出的程式碼流程圖,裡面詳細的介紹了整個備忘錄遞迴演算法的執行方向,介紹了hashmap如何存值,何時存值,如果細看的話,相信收穫頗豐。
最後再來說說動態規劃把,關於它的定義我就不再多費口舌,簡單概述,自下而上,把整個數想成一個數組,把a[0]+a[1]的值放在a[2],然後a[1]+a[2]的值放在a[3],以此類推,只要一個迴圈就可以得出答案了。整個方法因為沒有遞迴的再呼叫,所以只被呼叫一次,從而大大減少了執行時間,