遞推法與遞迴法
阿新 • • 發佈:2022-03-25
遞迴演算法
-
遞迴演算法是一種從自頂向下的演算法 ,實際上是通過不停的直接呼叫或者間接的呼叫自身的函式,通過每次改變變數完成多個過程的重複計算,直到到達邊界之後,結束呼叫。
-
與遞推法相似的是,遞迴與遞推都是將一個複雜過程分解為幾個簡單重複步驟進行計算。
-
實現的核心是分治策略,即分而治之,將複雜過程分解為規模較小的同類問題,通過解決若干個小問題,進而解決整個複雜問題。
-
遞迴演算法設計的一般步驟:
- 根據題目設計遞迴函式中的運算部分;
- 根據題目找到遞迴公式,題目可能會隱含給出,也可能需要自己進行推導;
- 找到遞迴出口,即遞迴的終止條件。
遞推演算法(迭代)
- 一個問題的求解需要大量重複計算,在已知的條件和所求問題之間總存在著某種相互聯絡的關係,在計算時,我們需要找到這種關係,進行計算(遞推關係式)。
- 即遞推法的關鍵,就是找到遞推關係式,這種處理方式能夠將複雜的計算過程,轉化為若干步驟的簡單重複運算。
例題1:
題目描述:
斐波那契數列(Fibonacci sequence),又稱黃金分割數列,因數學家萊昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖為例子而引入,故又稱為“兔子數列”。
指的是這樣一個數列:0、1、1、2、3、5、8、13、21、34、……
在數學上,斐波那契數列以如下被以遞推的方法定義:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
請求出該數列中第n個數字(n從1開始計數)是多少。
樣例:
輸入樣例
樣例輸入
6
輸出
8
對應的計算過程:
[0]=0
[1]=1
[2]=0+1
[3]=1+1=2
[4]=1+2=3
[5]=2+3=5
[6]=5+3=8
題目解析:
-
遞迴式,F(n)= F(n-1) + F(n-2)
-
F(n)=0 n=0 ,F(n)=1 n=1 這就是遞迴出口,能讓遞迴停止的條件。
遞推法實現:
import java.util.Scanner; public class Main { public static void main(String[] args) { int n; //第幾個數 int x=0; //F(n) int y=1; //F(n+1) int ans = 0; //F(n+2) Scanner in = new Scanner(System.in); n = in.nextInt(); if(n==0) ans=0; else if(n==1) ans=1; else { for(int i=2;i<=n;i++) { ans=x+y; x=y; y=ans; } } System.out.println(ans); } }
遞迴法實現:
import java.util.Scanner;
public class Main {
static int fn(int n)
{
if(n==0)
return 0;
//遞迴出口2
else if(n==1 )
return 1;
else
return fn(n-1)+fn(n-2); //遞迴關係式
}
public static void main(String[] args) {
int n; //第幾個數
int ans = 0;
Scanner in = new Scanner(System.in);
n = in.nextInt();
ans=fn(n);
System.out.println(ans);
}
}
例題2: (題目分儲存和非儲存的,若是需要多次詢問資料,就採取儲存的方式)
題目描述:
這樣一個數列:0、1、1、2、3、5、8、13、21、34、……
在數學上,斐波那契數列以如下被以遞推的方法定義:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。
我們將進行M次查詢,每次輸入一個N,其中n小於30。
請求出該數列中第n個數字(n從1開始計數)是多少?
樣例:
輸入樣例
樣例1輸入:
6
4
2
7
8
8
10
輸出樣例
樣例1輸出:
3
1
13
21
21
55
題目解析:
這道題跟上面一道題的演算法原理相同,只是增加了多次查詢的複雜度;
將每次訪問的結果都儲存在陣列中,陣列的下標正好與n對應起來
陣列的長度就是n的取值範圍;
遞推法實現:
import java.util.Scanner;
public class DiGui1 {
//將每次訪問的結果都儲存在陣列中,陣列的下標正好與n對應起來
static int[] result = new int[35];
//實現每次計算
static void danci() {
result[0] = 0;
result[1] = 1;
for(int i=2;i<=30;i++) {
result[i] = result[i-1] + result[i-2];
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int m = scanner.nextInt();//m次的查詢
danci(); //因為此函式不需要傳引數,執行一次,就將n從0到30的結果都儲存到結果陣列中,所以寫到每次迴圈的外面
//查詢m次
while(m>0) {
m--;
//要查詢的值對應的n,在迴圈體內輸入
int n = scanner.nextInt();
System.out.println(result[n]);
}
}
}
遞迴法實現:
import java.util.Scanner;
public class DiGui2 {
// 將每次訪問的結果都儲存在陣列中,陣列的下標正好與n對應起來
static int[] result = new int[35];
// 實現每次計算
static int danci(int n) {
// 遞迴出口1
if (n == 0) {
result[0] = 0;
return 0;
} else if (n == 1) {
result[1] = 1;
return 1;
} else {
result[n] = danci(n - 1) + danci(n - 2);
return result[n];
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int m = scanner.nextInt();// m次的查詢
// 因為遞迴底層運算是從n=0 一直到n=30,執行一次,就將n從0到30的結果都儲存到結果陣列中,所以寫到每次迴圈的外面
danci(30);
// 查詢m次
while (m > 0) {
m--;
// 要查詢的值對應的n,在迴圈體內輸入
int n = scanner.nextInt();
System.out.println(result[n]);
}
}
}
總結:
對於以上兩種方式實現儲存型的遞推與遞迴,都是先把各個結果得出,儲存到陣列中儲存起來,那多次訪問,就不用每次都計算一下。