证明方法
一:归纳法
1. 证明基准情形,就是确定定理对于某个(某些)小的(通常是退化的)值的正确性。
2. 进行归纳假设,一般说来,这意味着定理对直到某个有限数k的所有情况都是成立的
3. 使用上述假设证明定理对于下一个值(通常是k+1)也是成立的
二:反证法
1. 假设定理不成立
2. 证明基于上述假设导致已知性质或条件不成立,导出矛盾,证明定理是成立的
什么是递归?
当一个函数用自身来定义,就称为是递归函数。比如
int f(int x)
{
if(x==0)
return 0;
else
return 2*f(x-1)+x*x;
}
使用递归的基本法则:
1. 基准情形。必须总是有某些基准的情形,不需要使用递归就能求解。
2. 不断推进。对于那些要被递归求解的情形,递归调用必须总能够朝着一个基准情形推进
3. 设计法则。假设所有的递归调用都能运行。
4. 合成效益法则。在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。
注:前三条法则是保证一个递归调用正确运行的保证,而第四条是保证递归的效率。
递归的主要问题是隐藏的簿记开销,但是灵活使用递归能写出一些很精简巧妙的代码。下面通过一个例子来说明,递归的妙用。
一. 最大的子序列和问题:给定整数a1, a2, a3, a4...an可能有负数,求最大子序列和。
例如对于输入-2, 11,-4, 13, -5, -2,答案为20(从a2 到a4)
方法一:暴力求解(略)
方法二:递归求解
inline int max3(int a, int b, int c)
{
int max=a>b?a:b;
return max>c?max:c;
}
int maxSumRec(const vector<int>& v, int left, int right)
{
if(left==right){
if(v[left]>0)
return v[left];
else
return 0;
}
int center=(left+right)/2;
int leftMaxSum=maxSumRec(v, left, center);
int rightMaxSum=maxSumRec(v, center+1, right);
int leftBorderSum=0, leftMaxBorderSum=0;
for(int i=center;i>=left;--i)
{
leftBorderSum+=v[i];
if(leftBorderSum>leftMaxBorderSum)
leftMaxBorderSum=leftBorderSum;
}
int rightBorderSum=0, rightMaxBorderSum=0;
for(int i=center+1;i<=right;++i)
{
rightBorderSum+=v[i];
if(rightBorderSum>rightMaxBorderSum)
rightMaxBorderSum=rightBorderSum;
}
return max3(leftMaxSum, rightMaxSum,rightMaxBorderSum+leftMaxBorderSum);
}
int maxSubSum(const vector<int> v)
{
return maxSumRec(v, 0, v.size()-1);
}
方法三:
int maxSubSum(const vector<int>& v)
{
int thisSum=0, maxSum=0;
for(int i=0;i<v.size();++i)
{
thisSum+=v[i];
if(thisSum>maxSum)
maxSum=thisSum;
else if(thisSum<0)
thisSum=0;
}
return maxSum;
}
从中我们可以看到递归可以提高效率,相对于方法三,第二种方法四一种比较有效的方法,下面是另外一个使用递归的例子。
使用递归来高校求幂。
long pow(int x, int n)
{
if(n==0)
return 1;
if(n==1)
return x;
if(n%2==0)
return pow(x*x, n/2);
else
return pow(x*x, n/2)*x;
}
递归是一种很好的思维方式,可以简化编码,是代码更清晰,但是使用也得慎重,不要误用递归,下面就是递归的典型误用。
long fib(int n)
{
if(n<=1)
return 1;
else
return fib(n-1)+fib(n-2);
}
其时间复杂度为指数,明显违反了递归使用的第四条准则,做了很多重复性的工作。