1.2.1線形再帰
例-配列の合計
int sum(int A[],int n){
if(1>n){
//平凡情况,递归基
return 0;//直接计算(非递归式子)
}
else{
//一般情况
return sum(A,n-1)+A[n-1];//递归前n-1之项和,再累计计算n-1项
}
}//O(1)*递归深度 =O(1)*(n+1)=O(n)
線形再帰
合計アルゴリズムはそれ自体をより深いレベルに呼び出すことができ、各再帰インスタンスは最大で1回それ自体を呼び出します。したがって、各レベルには最大で1つのインスタンスがあります。そして、それらは線形の順序関係を形成します。したがって、このタイプの再帰モードは「線形再帰」(線形再帰)と呼ばれ、最も基本的な再帰モードでもあります。
削減して征服する
再帰のレベルごとに、解決する問題の規模は、それが些細な小さな(単純な)問題に退化するまで定数だけ減少します。
1.2.1再帰的分析
再帰的追跡
- アルゴリズムの各再帰インスタンスは、インスタンス呼び出しのパラメーターを示すボックスとして表されます。
- インスタンスMがインスタンスNを呼び出したい場合は、MとNに対応するボックスの間に有向線を追加します
漸化式
再帰モデルの数学的帰納法により、複雑さの限界関数とその境界条件の再帰方程式(セット)が導出され、複雑さの分析が再帰方程式(セット)の解に変換されます。
例-intsum(int A []、int n)関数のスペースの複雑さを計算します
アルゴリズムを使用して、長さnの配列の合計をカウントするとします。必要なスペースの量はS(n)であるため、漸化式はほぼ次のようになります。
S(1)=O(1);
S(n)=S(n-1)+O(1);
两式联合求解可得
S(n)=O(n);
1.2.3再帰モード
- 複数の再帰ベース-複数の再帰ベースがあります
- 再帰を実装する-サブ問題が元の問題と同じ意味に分割されていることを確認します
- 多方向再帰-再帰呼び出しには複数の代替ブランチがあります(区分的関数など)
1.2.4再帰的除去
スペースコスト
同じアルゴリズムの反復バージョンと比較すると、再帰バージョンは多くの場合、より多くのスペースを必要とし、それが実際の実行速度に影響します。さらに、オペレーティングシステムに関する限り、再帰呼び出しを実装するために再帰インスタンスを作成、維持、および削除するには多くの時間がかかり、計算負荷もさらに悪化します。したがって、実行速度とストレージスペースを最適化するために、非再帰的な同等のアルゴリズムがよく使用されます。
末尾再帰とその除去
線形再帰アルゴリズムでは、再帰呼び出しが再帰インスタンスの最後の操作として表示される場合、それは末尾再帰と呼ばれます。たとえば、次のコードでは、アルゴリズムの最後のステップは、最初と最後の要素を削除した後、全長を2単位短縮したサブ配列を再帰的に反転することです。これは、典型的な末尾再帰です。
void reverse(int *A,int lo,int hi){
if(lo<hi){
swap(A[lo],A[hi]);//交换A[lo]和A[hi]
reverse(A,li+1,hi-1);//递归倒置A(lo,hi)
}//else包含两种递归基
}//O(li-hi+1)
1.2.5二分再帰
分割統治
大きな問題はいくつかの小さな問題に分解され、再帰的なメカニズムによって個別に解決されます。この分解は、サブ問題のサイズが通常の状況に縮小されるまで続きます。
配列の合計-分割統治
int sum(int A[],int lo, int hi){
if(lo==hi){
return A[lo];
}
else{
//否则,一般情况下lo<hi,则
int mi=(lo+hi)>>1;//左移一位,以居中单位为界,将原区一份为二
return sum(A,lo,mi)+sum(A,mi+1,hi);//递归对各子数组求和,然后合计
}
}//O(hi-li+1),线性正比于区间的长度
インスタンス
フィボナッチ数:2部再帰
递归形式
fib(n)= {n、n <= 1 fib(n − 1)+ fib(n − 2)、n> = 2 fib(n)= \ begin {cases} n、n <= 1 \\ fib(n-1)+ fib(n-2)、n> = 2 \ end {cases}f i b (n )={{
n 、n< =1f i b (n−1 )+f i b (n−2 )、n> =2
_int64 fib(int n){
//计算Fibonacci数列的第n项(二分递归版)
return (2>n)?
(_int 64) n//若能到达递归基,直接取指
: fib (n-1)+ fib(n-2);//否则递归计算前两项,其和即为正解
}
最適化戦略-メモリ検索と同様
一定量の補助スペースを使用して、各サブ問題が解決された後、対応する回答が時間内に記録されます
たとえば、次の線形再帰バージョンのfibシーケンス
_int64 fib(int n,_int64 &prev ){//计算Fibonacci数列的第n项(线性递归版): 入口形式()
if(0==n){//若能到达递归基
prev=1;
return 0;//直接取值fib(-1)=1,fib(0)=0;
}
else{
_int64 prevPrev;
prev=fib(n-1,prevPrev);//递归计算前两项
return prevPrev+prev;//其和即为正解
}
}//用辅助变量记录前一项,返回数列的当前项,O(n);
行列の高速パワーを使用してlogNの複雑さを取得できるフィボナッチ数列クラスの例
class Fib{
private:
int f, g;//f=fib(k-1),g=fib(k)均为int型,很快就会数值溢出
public:
Fib(int n){
//初始化不小于n的最小Fibonacci项
f=1;g=0;
while(g<n) next();
}
int get(){
return g;}
int next(){
g+=f;f=g-f;return g;}
int prev(){
f=g-f;g-=f;return g;}
};
マトリックス高速電力ソリューションフィボナッチ数列
public int[][] matrixPower(int[][] m, int p){
//矩阵快速幂
int[][] res = new int[m.length][m[0].length];
//先把res初始化为单位矩阵
for (int i = 0; i < res.length; i++){
res[i][i] = 1;
}
int[][] tmp = m;
for (; p != 0; p >>= 1){
//右移符号,相当于整除2
if ((p&1) != 0){
res = muliMatrix(res, tmp);
}
tmp = muliMatrix(tmp, tmp);
}
return res;
}
public int[][] muliMatrix(int[][] m1, int[][] m2){
//矩阵乘法
int[][] res = new int[m1.length][m2[0].length];
for (int i = 0; i < m2[0].length; i++){
for (int j = 0; j < m1.length; j++) {
for (int k = 0; k < m2.length; k++){
res[i][j] += m1[i][k] * m2[k][j];
}
}
}
return res;
}
public int calc(int n){
//使用矩阵快速幂求解Fibonacci数列
if (n < 1) {
return 0;
}
if (n == 1 || n == 2){
return 1;
}
int[][] base = {
{
1, 1} , {
1, 0}};
int[][] res = matrixPower(base, n - 2);
return res[0][0] + res[1][0];
}