正文

分治法2007-03-20 10:37:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/lingdlz/24118.html

分享到:

 /*   分治法简介
 
    任何一个可以用计算机求解的问题所需的计算时间都与其规模
有关。问题的规模越小,越容易直接求解,解题所需的计算时间也
越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。
n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。
而当n较大时,问题就不那么容易处理了。要想直接解决一个规模
较大的问题,有时是相当困难的。

    分治法的设计思想是,将一个难以直接解决的大问题,分割成一
些规模较小的相同问题,以便各个击破,分而治之。

    分治策略是:对于一个规模为n的问题,若该问题可以容易地解决
(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的
子问题,这些子问题互相独立且与原问题形式相同,递归地解这些
子问题,然后将各子问题的解合并得到原问题的解。这种算法设计
策略叫做分治法。

    如果原问题可分割成k个子问题,1<k≤n ,且这些子问题都可解
并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行
的。由分治法产生的子问题往往是原问题的较小模式,这就为使用递
归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问
题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容
易直接求出其解。这自然导致递归过程的产生。分治与递归像一对孪
生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。

    分治法所能解决的问题一般具有以下几个特征:
1) 该问题的规模缩小到一定的程度就可以容易地解决
2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子
   结构性质。
3) 利用该问题分解出的子问题的解可以合并为该问题的解;
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公
   共的子子问题。

    上述的第一条特征是绝大多数问题都可以满足的,因为问题的计算复
杂性一般是随着问题规模的增加而增加;第二条特征是应用分治法的前提
它也是大多数问题可以满足的,此特征反映了递归思想的应用;第三条特
征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具
备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或
动态规划法。第四条特征涉及到分治法的效率,如果各子问题是不独立的
则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用
分治法,但一般用动态规划法较好。
    分治法的基本步骤
分治法在每一层递归上都有三个步骤:

分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的
      子问题;

解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题

合并:将各个子问题的解合并为原问题的解。

它的一般的算法设计模式如下:
Divide-and-Conquer(P)
1.  if |P|≤n0 
2.    then return(ADHOC(P))
3.  将P分解为较小的子问题 P1 ,P2 ,...,Pk
4.  for i←1 to k
5.    do yi ← Divide-and-Conquer(Pi)    △ 递归解决Pi
6.  T ← MERGE(y1,y2,...,yk)             △ 合并子问题
7.  return(T)
其中|P|表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问
题已容易直接解出,不必再继续分解。ADHOC(P)是该分治法中的基本子算法,
用于直接解小规模的问题P。因此,当P的规模不超过n0时直接用算法ADHOC(P)
求解。算法MERGE(y1,y2,...,yk)是该分治法中的合并子算法,用于将P的子问
题P1 ,P2 ,...,Pk的相应的解y1,y2,...,yk合并为P的解。

根据分治法的分割原则,原问题应该分为多少个子问题才较适宜?
各个子问题的规模应该怎样才为适当?

答:  但人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模
大致相同。换句话说,将一个问题分成大小相等的k个子问题的处理方法是行之
有效的。许多问题可以取 k = 2。这种使子问题规模大致相等的做法是出自一
种平衡(balancing)子问题的思想,它几乎总是比子问题规模不等的做法要好。

                                                               出处:网络
 
实践题目: 


给定一个顺序表,编写一个求出其最大值和最小值的分治算法。


分析:
由于顺序表的结构没有给出,作为演示分治法这里从简顺序表取一整形数组
数组大小由用户定义,数据随机生成。我们知道如果数组大小为 1 则可以
直接给出结果,如果大小为 2则一次比较即可得出结果,于是我们找到求解
该问题的子问题即: 数组大小 <= 2。到此我们就可以进行分治运算了,只要
求解的问题数组长度比 2 大就继续分治,否则求解子问题的解并更新全局解
以下是代码。

*/

/*** 编译环境TC ***/

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define M 40

/* 分治法获取最优解 */
void PartionGet(int s,int e,int *meter,int *max,int *min){
/* 参数:
 * s 当前分治段的开始下标
 * e  当前分治段的结束下标
 * meter 表的地址
 * max 存储当前搜索到的最大值
 * min 存储当前搜索到的最小值
 */
 int i;

 if(e-s <= 1){ /* 获取局部解,并更新全局解 */
  if(meter[s] > meter[e]){
   if(meter[s] > *max)
    *max = meter[s];
   if(meter[e] < *min)
    *min = meter[e];
  }
  else{
   if(meter[e] > *max)
    *max = meter[s];
   if(meter[s] < *min)
    *min = meter[s];
  }
  return ;
 }
 i = s + (e-s)/2; /* 不是子问题继续分治,这里使用了二分,也可以是其它 */
 PartionGet(s,i,meter,max,min);
 PartionGet(i+1,e,meter,max,min);
}


int main(){
 int i,meter[M];
 int max = INT_MIN;   /* 用最小值初始化 */
 int min = INT_MAX;   /* 用最大值初始化 */

 printf("The array's element as followed:\n\n");
 randomize();    /* 初始化随机数发生器 */
 for(i = 0; i < M; i ++){  /* 随机数据填充数组 */
  meter[i] = rand()%10000;
  if(!((i+1)%10))   /* 输出表的随机数据 */
   printf("%-6d\n",meter[i]);
  else
   printf("%-6d",meter[i]);
 }
 PartionGet(0,M - 1,meter,&max,&min); /* 分治法获取最值 */
 printf("\nMax : %d\nMin : %d\n",max,min);
 system("pause");
 return 0;
}

阅读(5737) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册