1.递归的概念
-
递归算法
直接或间接地调用自身的算法称为递归算法。 -
递归函数
用函数自身给出定义的函数称为递归函数。 -
由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。这自然导致递归过程的产生。
-
分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
①阶乘函数
递归算法:
int f1(int n)
{
if(n==0)
return 1;
return n*f1(n-1);
}
消除递归:
int f2(int n)
{ int m=1;
for(int i=1;i<=n;i++)
m=m*i;
return m; }
②Fibonacci数列
递归算法:
int f1(int n)
{
if(n<=1)
return 1;
return f1(n-1)+f1(n-2);
}
消除递归:
int f2(int n)
{ int m0=1,m1=1,m2;
for(int i=2;i<=n;i++)
{ m2=m0+m1;
m0=m1;
m1=m2; }
return m2;
}
利用通项公式求𝑭(𝒏):
int f3(int n)
{ double x,y;
x=0.5(sqrt(5.0)+1);
y=pow(x,n+1)/sqrt(5.0);
return y+0.5;
}
Ackerman函数——双递归函数
当一个函数及它的一个变量由函数自身定义时,称这个函数为双递归函数。
Ackerman函数𝑨(𝒏,𝒎)有两个独立的整型变量𝒎≥𝟎和𝒏≥𝟎,其定义如下:
𝑨(𝒏,𝒎)的自变量𝒎的每一个值都定义了一个单变量函数。
(1)当𝒎=𝟏时
∵𝑨(𝟏,𝟏)=𝑨(𝑨(𝟎,𝟏),𝟎)=𝑨(𝟏,𝟎)=𝟐
𝑨(𝒏,𝟏)=𝑨(𝑨(𝒏−𝟏,𝟏),𝟎)=𝑨(𝒏−𝟏,𝟏)+𝟐(n>1)
∴𝑨(𝒏,𝟏)=𝟐𝒏 (𝒏≥𝟏)
即𝑨(𝒏,𝟏)是函数乘2
(2)当𝒎=𝟐时
∵𝑨(𝒏,𝟐)=𝑨(𝑨(𝒏−𝟏,𝟐),𝟏)=𝟐𝑨(𝒏−𝟏,𝟐) (n>1)
𝑨(𝟏,𝟐)=𝑨(𝑨(𝟎,𝟐),𝟏)=𝑨(𝟏,𝟏)=𝟐
∴𝑨(𝒏,𝟐)=𝟐^𝒏
类似地可以推出,𝑨(𝒏,𝟑)=𝟐^(𝟐^(…^𝟐 ) ),其中2的层数是n
𝑨(𝒏,𝟒)的增长速度非常快,没有适当的数学式来表示。
递归过程和次数
int ct=0,a[65535][5]={0};
//用全局变量ct记递归调用次数
//a[n][m]记A1(n,m)被调用的次数
int A1(int n,int m)
{ a[n][m]++; ct++;
if(n==1&&m==0) return 2;
if(n==0) return 1;
if(m==0) return n+2;
return A1(A1(n-1,m),m-1); }
运行问题:
1、溢出→𝒏,𝒎较大时
2、递归次数多
2.分治法的基本思想
基本思想
将一个规模为𝒏的问题分解为𝒌个规模较小的子问题,这些子问题互相独立且与原问题相同。
递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
以大整数的乘法为例,说明分治法。
//大整数乘法模拟实现
#include <iostream>
using namespace std;
int mul(int X,int Y,int n,int &ct) //ct记1位乘法次数。
{
int a,b,c,d,p,q,r,w,i;
if(n==1)
{
cout<<endl<<"ct="<<ct++<<" "<<X<<"*"<<Y<<"="<<X*Y;
return X*Y;
}
w=10;
for(i=1;i<n/2;i++)
w=10*w; //分拆为高位和低位两部分。
a=X/w;b=X%w;c=Y/w;d=Y%w;
cout<<endl<<"a="<<a<<"\nb="<<b<<"\nc="<<c<<"\nd="<<d<<endl;
p=mul(a-b,d-c,n/2,ct);
q=mul(a,c,n/2,ct);
r=mul(b,d,n/2,ct);//分治及合成结果。
//cout<<endl<<q*w*w+(p+q+r)*w+r<<endl;
return (q*w*w+(p+q+r)*w+r);
}
int main()
{
int x,y,n,ct=1;
x=9118;y=3281;n=4;
mul(x,y,n,ct);
}