【分治算法】
是一种思想,你拿到的问题往往不会是很简单的事情,而再复杂再难的问题都可以分解成一些有限的简单问题的和,这就是分治法的原理。
举个最简单的例子,计算阶乘之和n!+(n-1)!+...+1!:
n可以取大取小,问题的范围可以大可以小,如果小就比较容易,比如1!=1这是己知,如何将大问题化成小问题,找它们之间的联系:
如果n=k已经解决,那么n=k+1能否解决,如何解决?
很显然:n=k+1的情况就是将n=k的结果加上(k+1)!的值。
【递归程序设计】
是一种算法常用设计方法,在高级语言中,函数之间可以进行嵌套调用,用栈的机制实现,这样的做法可以使我们在算法设计的时候能够达到套调算法设计。
int facsum(int n)
{
if(n==1)return 1;
return fac(n)+facsum(n-1);
}
/*fac()为求阶乘函数,facsum()为阶乘求和函数*/
“if(n==1)return 1;”为小问题、简单问题解的直接描述,在递归中称为递归边界,没有设定边界的递归是只能‘递’不能‘归’的,即死循环。
“return fac(n)+facsum(n-1);”中将n的范围缩小:n-1,以此将大问题化为类似的小问题,类似很重要,如果不类似就不能调同一个函数,这很显然。——这样一个过程就是分治算法的实现。
[阶乘求和可以用更高效率的算法求解,以上只是做为一个简单例子]
再举一例,将数组的元素逆置:
void inverse(int a[],int n)
{
if(__①__)return;
swap(a[0],a[n-1]);/*swap(a,b)为交换a,b的值*/
inverse(__②__);
}
①处为小问题解描述,也是递归边界,怎样的小问题是很显然的呢?当数组的元素个数小于2时是不需要逆置的,所以填:n<2
②交换之后进行递归,分解成小问题,两端的已经交换,所以往中间靠,所以填:a+1,n-2
这样的问题有很多很多,如拆半法查找,用二分法求方程的根等等。都离不开两点:1、小问题的描述,即递归边界,2、将大问题化成小问题。
拆半法查找的小问题是:low==high;二分法求根的小问题是fabs(x1-x2)<eps。
PS:再加点吧,我竟没有想到汉诺塔这么经典的递归设计问题,有很多初学朋友不太明白程序的意图,你可以借助我上面讲的加深理解,书中的经典程序大致如下:
void move(int n,int from,int med,int to)
{
if (n==1)
printf("%d-->%d/n",from,to);
else
{
move(n-1,from,to,med);
printf("%d-->%d/n",from,to);
move(n-1,med,from,to);
}
}
这是一个核心函数,
if(n==1)即小问题描述,也就是递归边界
“
move(n-1,from,to,med);
printf("%d-->%d/n",from,to);
move(n-1,med,from,to);
”
是把问题的规模化小,也就是分而治之的思想,不管过程有多复杂,只要能把问题的规模化小,就可以运用分治+递归设计程序。
是一种思想,你拿到的问题往往不会是很简单的事情,而再复杂再难的问题都可以分解成一些有限的简单问题的和,这就是分治法的原理。
举个最简单的例子,计算阶乘之和n!+(n-1)!+...+1!:
n可以取大取小,问题的范围可以大可以小,如果小就比较容易,比如1!=1这是己知,如何将大问题化成小问题,找它们之间的联系:
如果n=k已经解决,那么n=k+1能否解决,如何解决?
很显然:n=k+1的情况就是将n=k的结果加上(k+1)!的值。
【递归程序设计】
是一种算法常用设计方法,在高级语言中,函数之间可以进行嵌套调用,用栈的机制实现,这样的做法可以使我们在算法设计的时候能够达到套调算法设计。
int facsum(int n)
{
if(n==1)return 1;
return fac(n)+facsum(n-1);
}
/*fac()为求阶乘函数,facsum()为阶乘求和函数*/
“if(n==1)return 1;”为小问题、简单问题解的直接描述,在递归中称为递归边界,没有设定边界的递归是只能‘递’不能‘归’的,即死循环。
“return fac(n)+facsum(n-1);”中将n的范围缩小:n-1,以此将大问题化为类似的小问题,类似很重要,如果不类似就不能调同一个函数,这很显然。——这样一个过程就是分治算法的实现。
[阶乘求和可以用更高效率的算法求解,以上只是做为一个简单例子]
再举一例,将数组的元素逆置:
void inverse(int a[],int n)
{
if(__①__)return;
swap(a[0],a[n-1]);/*swap(a,b)为交换a,b的值*/
inverse(__②__);
}
①处为小问题解描述,也是递归边界,怎样的小问题是很显然的呢?当数组的元素个数小于2时是不需要逆置的,所以填:n<2
②交换之后进行递归,分解成小问题,两端的已经交换,所以往中间靠,所以填:a+1,n-2
这样的问题有很多很多,如拆半法查找,用二分法求方程的根等等。都离不开两点:1、小问题的描述,即递归边界,2、将大问题化成小问题。
拆半法查找的小问题是:low==high;二分法求根的小问题是fabs(x1-x2)<eps。
PS:再加点吧,我竟没有想到汉诺塔这么经典的递归设计问题,有很多初学朋友不太明白程序的意图,你可以借助我上面讲的加深理解,书中的经典程序大致如下:
void move(int n,int from,int med,int to)
{
if (n==1)
printf("%d-->%d/n",from,to);
else
{
move(n-1,from,to,med);
printf("%d-->%d/n",from,to);
move(n-1,med,from,to);
}
}
这是一个核心函数,
if(n==1)即小问题描述,也就是递归边界
“
move(n-1,from,to,med);
printf("%d-->%d/n",from,to);
move(n-1,med,from,to);
”
是把问题的规模化小,也就是分而治之的思想,不管过程有多复杂,只要能把问题的规模化小,就可以运用分治+递归设计程序。