分治法基本思想
- 将一个难以直接解决的大问题,分割成一些规模较小的k个相同问题,以便·各个击破,分而治之·。
- 对这k个子问题分别求解。如果子问题的规模仍然不够小,则再划分为若干个子问题,如此
递归
的进行下去,直到问题规模足够小,很容易求出其解为止。 - 将求出的小规模的问题的解合并为一个更大规模的问题的解,
自底向上逐步求出原来问题的解
。
递归是实现分治算法思想的技术
。👉查看递归的介绍
合并排序的例子
分治法的适用条件
分治法所能解决的问题一般具有以下几个特征
:
- 该问题的规模缩小到一定的程度就可以容易地解决
因为问题的计算复杂性一般是随着问题规模的增加而增加,大部分问题满足这个特征。
- 该问题可以分解为若干个规模较小的
相同
问题
这条特征是应用分治法的前提
,它也是大多数问题可以满足的,此特征反映了递归思想的应用
- 利用该问题分解出的子问题的解可以合并为该问题的解
能否利用分治法完全取决于问题是否具有这条特征,如果具备了前两条特征,;而不具备第三条特征,则可以考虑贪心算法
或动态规划
。
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题
如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然也可用分治法,但一般用动态规划
较好。
分治法的基本步骤
divide-and-conquer(P)
{
if ( | P | <= n0) adhoc(P); //解决小规模的问题, adhoc(P)是基本子算法
divide P into smaller subinstances P1,P2,...,Pk;//分解问题
for (i=1,i<=k,i++)
yi=divide-and-conquer(Pi); //递归的解各子问题
return merge(y1,...,yk); //将各子问题的解合并为原问题的解
}
| P | 表示问题的规模,n0是规模的阈值,表示当问题P的规模不超过n0时,直接用adhoc§算法求解,不必继续分解。
实践表明,在用分治法设计算法时,最好使子问题的规模大致相同
。即将一个问题分成大小相等的k个子问题的处理方法是行之有效的。这种使子问题规模大致相等的做法是出自一种平衡(balancing)
子问题的思想,它几乎总是比子问题规模不等的做法要好。
分治法的复杂性分析
- 分治法将规模为n的问题,分成k个规模为n/m的子问题去解。当然
并不一定都是n/m
。参考快速排序的最坏情况的复杂度 - 设分解规模阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。
- 再设将原问题分解( divide )为k个子问题,以及用merge将k个子问题的解合并(merge)为原问题的解,需用f(n)个单位时间。
用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间,则有:
通过迭代法求得方程的解:
求解步骤:
大整数的乘法
请设计一个有效的算法,可以进行两个n位二进制大整数的乘法运算
-
小学的方法:O(n2) ×效率太低
-
分治法:XY = ac 2n + (ad+bc) 2n/2 + bd O(n^2) ×效率太低
为了降低时间复杂度,必须减少乘法的次数。
-
XY = ac 2^n + ((a-b)(d-c)+ac+bd) 2^n/2 + bd
-
XY = ac 2^n + ((a+b)(d+c)-ac-bd) 2^n/2 + bd
细节问题:两个XY的复杂度都是O(nlog3),但考虑到a+b,d+c可能得到m+1位的结果,使问题的规模变大,
故不选择第2种方案
。
复杂度分析
参考上面的分治复杂度一般公式
Strassen矩阵乘法
若依此定义来计算A和B的乘积矩阵C,则每计算C的一个元素C[i][j],需要做n次乘法和n-1次加法。因此,算出矩阵C的n2个元素所需的计算时间为O(n3)
- 传统方法:O(n^3)
- 分治法:
使用与上例类似的技术,将矩阵A,B和C中每一矩阵都分块成4个大小相等的子矩阵。由此可将方程C=AB重写为:
由此可得:
复杂度分析
为了降低时间复杂度,必须减少乘法的次数。
复杂度分析
Hopcroft和Kerr已经证明(1971),计算2个2×2矩阵的乘积,7次乘法是必要的
。因此,要想进一步改进矩阵乘法的时间复杂性,就不能再基于计算2×2矩阵的7次乘法这样的方法了。或许应当研究3×3或5×5矩阵的更好算法
在Strassen之后又有许多算法改进了矩阵乘法的计算时间复杂性。目前最好的计算时间上界是 O(n2.376)