分治的实现::递归程序设计

时间:2021-04-04 23:39:29
【分治算法】
是一种思想,你拿到的问题往往不会是很简单的事情,而再复杂再难的问题都可以分解成一些有限的简单问题的和,这就是分治法的原理。

举个最简单的例子,计算阶乘之和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);

是把问题的规模化小,也就是分而治之的思想,不管过程有多复杂,只要能把问题的规模化小,就可以运用分治+递归设计程序。