1. 分治法
1.1 算法描述:
分治法(divide and conquer algorithms, D&C )是使用一种“分而治之”的思想。好比古代一个国家的土地,分给了多个诸侯共同治理的过程。
分治法的精髓在于三个步骤:
分 – 将问题分解成规模更小的子问题;
治 – 对每个子问题分别采用相同的方法逐一击破;
合 – 将已解决的子问题的解合并起来得到母问题的解。
一般的,在程序上,分治法通常通过递归来实现。类似的,像排序算法中的快速排序、归并排序等。但是并不是所有递归,都是一个最好的分治算法,如下面的斐波那契数列的例子。
分治法,最难的一个步骤是如何进行原问题分解成规模更小的子问题。而算法的效率往往取决于分解得到后子问题的规模。每一步分解得到的子问题规模越小,则算法的效率越快。假设一个原问题规模为n,则它的子问题规模一般可能k和(n-k)。此处k<=n。而我们在算法处理工程中,有可能两部分都要处理,也有可能只需要处理某一部分的问题。
1.2 算法应用
1.2.1 乘法问题(powing a number)
问题描述:给出一个数字x和正整数n(n>=0), 计算xn.。
- 方法一: xn. = x·x·x·x·x…, 算法复杂度:
;
- l 方法二(分治法):
算法复杂度:
1.2.2 斐波那契数列(Fibonacci sequence)
问题描述:给出整数n,计算出F(n), F(n)符合斐波那契数列要求;
斐波那契数列公式:
- l 方法一:递归处理
递归算法ALG(n):对输入n,若n等于0,函数返回0;对输入n,若n等于1,函数返回1;否则,返回递归处理结果:ALG(n-2)+ALG(n-1);
算法复杂为:
这个算法是非常慢的(幂指数级别),因为它虽然递归了,但是递归的子问题规模衰减太慢了。而且很明显看出,每次递归处理,实际上做了一些重复工作,比如F(n-1)的子问题其实进一步划分又需要求F(n-2)的解。
- l 方法二:
先算出F(0),F(1), F(2), 然后依次算出F(n-2)和F(n-1),最后再求和算出F(n)。
这个方法相比第一个方法的好处就是不用做更多的重复工作。
算法复杂度为。
- l 方法三:
利用斐波那契数列的性质:
对于分子,可以利用乘法问题的分治思想来解决,算法复杂度为。很遗憾,因为计算机处理浮点数时,往往精度不够,所以并不是最理想的方案。
- l 方法四
利用斐波那契数列的性质:
算法复杂度为。
1.2.3 矩阵乘法问题
问题描述:
对于输入矩阵,求矩阵
,其中
。
- l 方法一(直接矩阵乘法):
算法:
C矩阵元素个数为n*n,每个元素要进行n次乘法并累加得到,所以算法复杂度为。
- 方法二(分治法)
思路:将规模为n*n矩阵C分解为2*2子矩阵相乘,每个子矩阵规模为n/2 * n/2,如:
那么,有一下公式:
r = ae + bg
s = af + bh
t = ce + dg
u = cf + dh
输入算法复杂度:
然而算到这里,方法二并没有比第一个方法快。但是,聪明的人从上式子找到了突破口,思路是将乘法次数减少,现在进行了8次的乘法,只要我们乘法次数小于8那么我们的算法就能比原来的快。
- 改进后的算法:
P1 = a ·(f-h)
P2 = (a+b) ·h
P3 = (c+d) ·e
P4 = d ·(g-e)
P5 = (a+b)·(e+h)
P6 = (b-d)·(g+h)
P7 = (a-c)·(e+f)
r = P5 + P4 – P2 + P6
s= P1 + P2
t= P3 + P4
u= P5 + P1- P3 – P7
改进后的算法复杂度: