首页 > 代码库 > 分治法

分治法

分治法就是将一个难以解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之,递归法是分治法的实现手段。

问题:假定给出一个装16个硬币的袋子,袋子中有一个伪造的硬币,其质量比真硬币轻,现在任务是找出这个伪造的硬币。为了完成这个任务,将提供一台可用来比较两组硬币重量的仪器,利用这台仪器,可以知道两组硬币的重量是否相同。

1)穷举法。

两两比较硬币质量,若两硬币质量相等,则继续比较其他硬币。依此类推,最多通过8次比较便可以确定出伪造的硬币。

穷举法的基本思想是在可能的解空间中逐一尝试,通过判断尝试的值是否与已知条件矛盾来确定其是否为问题的解。这种思想通常不需要知道输入条件与问题的解之间的关系。即在对问题求解毫无头绪情况下总可以使用穷举法来解决。

从穷举法的定义可以看出,适用于穷举法的问题必须是可预先确定解的个数,解变量的值的取值范围预先可以确定。

同递归法一样,穷举法也是利用计算算计的高速度和不知疲惫特性来解决不容易找或找不到求解方程的问题。

 

2)分治法

将16个硬币看成一个大问题。

第一步,把这一问题分成2个小问题。随机选择8个硬币作为A组,剩下的8个硬币作为B组,这样就把16个硬币分成两个8枚硬币的问题。

第二步,判断A/B组中是否有假币,若两组硬币质量相等,说明没有假币;若不等,说明轻的那组有假币。

第三步,将第二步中的结果得出原先16个硬币问题的答案。若仅仅判断硬币是否存在,则第三步非常简单。无论A组还是B组中有假币,都可以推断出16个硬币存在假币,因此只通过一次质量比较,就可以判断假币是否存在。

 

对于一个规模为n的大问题,可以通过分解为k个规模较小的互相独立且与原问题结构相同的子问题。首先通过递归来求解这些子问题,然后对各子问题的解进行合并得到原问题的解,这种思路就是 分治法。

 

利用分治法求解问题,应同时满足如下4点要求.

1)原问题在规模缩小到一定程度时可以很容易地求解。

2)原问题可以分解为若干个规模较小的同构子问题。(这一点是分治法的前提,这反映了递归思想。满足该要求的问题通常称为该问题具有最优子结构性质)

3)各子问题的解可以合并为原问题的解。它决定了问题的求解是否可以利用分治法,如果这点得不到保证,通常会考虑贪心算法或动态规划法。

4)原问题所分解出的各个子问题之间是相互独立的。这涉及到分治法的效率,如果各个子问题是不独立的,则分治法要做许多不必要的工作,对公共的子问题进行重复操作,此时通常会考虑使用后面讲的动态规划。

 

二分查找、归并排序和快速排序用了分治法的一些思想。

二分法求方程近似解:求方程f(x)=x*x*x+x*x-1=0在[0,1]上的近似解,精确度为0.01.

算法分析:二分法求方程近似解的基本思想是将方程的有解区间平分为两个小区间,然后判断解在哪个小区间;继续把有解的区间一分为二进行判断,如此周而复始,直到求出满足精确要求的近似解。

#include<iostream>
using namespace std;
double Func(double a,double b,double c,double d,double x) {//函数表达式 
    return a*x*x*x+b*x*x+c*x+d;
}

double FindRoot(double a,double b,double c,double d,double low,double high,double e) {
    double mid=(low+high)/2;
    if(Func(a,b,c,d,mid)==0)
        return mid;
    while((high-low)>=e) {
        mid=(low+high)/2;
        if(Func(a,b,c,d,mid)==0)
            return mid;
        if(Func(a,b,c,d,low)* Func(a,b,c,d,mid) < 0 )
            high = mid;
        else
            low = mid;
    }
    return low;
}
  
int main() {
    //计算:f(x)=x*x*x+x*x-1=0在[0,1]上的近似解,精确度为0.01 
    double root=FindRoot(1.0,1.0,0,-1,0,1,0.01);
    cout<<"The root is"<<root<<endl;
    return 0;
}

 

分治法