分治算法
简介
对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。
分治法的基本思想
任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的。
分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
如果原问题可分割成k个子问题,1<k≤n ,且这些子问题都可解,并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的。由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。这自然导致递归过程的产生。分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
分治法的适用条件
分治法所能解决的问题一般具有以下几个特征:
该问题的规模缩小到一定的程度就可以容易地解决;
该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
利用该问题分解出的子问题的解可以合并为该问题的解;
该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
上述的第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;第二条特征是应用分治法的前提,它也是大多数问题可以满足的,此特征反映了递归思想的应用;第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑贪心法或动态规划法。第四条特征涉及到分治法的效率,如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
实例
二分查找
二分查找是一种算法,其输入是一个有序的元素列表(必须是有序的),如果查找的元素包含在列表中,二分查找返回其位置,否则返回NULL。
二分查找算法的原理如下:
1. 如果待查序列为空,那么就返回-1,并退出算法;这表示查找不到目标元素。
2. 如果待查序列不为空,则将它的中间元素与要查找的目标元素进行匹配,看它们是否相等。
3. 如果相等,则返回该中间元素的索引,并退出算法;此时就查找成功了。
4. 如果不相等,就再比较这两个元素的大小。
5. 如果该中间元素大于目标元素,那么就将当前序列的前半部分作为新的待查序列;这是因为后半部分的所有元素都大于目标元素,它们全都被排除了。
6. 如果该中间元素小于目标元素,那么就将当前序列的后半部分作为新的待查序列;这是因为前半部分的所有元素都小于目标元素,它们全都被排除了。
7. 在新的待查序列上重新开始第1步的工作。
二分查找之所以快速,是因为它在匹配不成功的时候,每次都能排除剩余元素中一半的元素。因此可能包含目标元素的有效范围就收缩得很快,而不像顺序查找那样,每次仅能排除一个元素。
function [result] = Binary_Search(L,a,b,x) %UNTITLED 此处显示有关此函数的摘要 % 此处显示详细说明 if a>b result=-1; else m=floor((a+b)/2); if x==L(m) result=m; else if x>L(m) result=Binary_Search(L,m+1,b,x); else result=Binary_Search(L,a,m-1,x); end end end end
我们针对数组a=[1 3 5 7 9 10 12 15 16 20];
最大子数组问题
给定一个数组,寻找数组中的和最大的非空连续子数组。下图为 《算法导论》第4章 内容。
使用分治策略的求解方法
我们来思考如何用分治技术来求解最大子数组问题。假定我们要寻找子数组A[low. high]的最大子数组。使用分治技术意味着我们要将子数组划分为两个规模尽量相等的子数组。也就是说,找到子数组的中央位置,比如mid,然后考虑求解两个子数组A[Low.. mid]和A[mid+
1.. high]。A[lowe . high]的任何连续子数组A[i..j]门所处的位置必然是以下三种情况之一:
1 完全位于子数组A[low. mid]中,因此low<=i<=j<=mid.
2 完全位于子数组A[mid+1..high]中,因此mid<i<=j<=high.
3 跨越了中点,因此low<ismid<j<high.
因此, A[low. high]的一个最大子数组所处的位置必然是这三种情况之一。实际上,A[low. high]的一个最大子数组必然是完全位于A[low..mid]中、完全位于A[mid+ 1.. high中或者跨越中点的所有子数组中和最大者。我们可以递归地求解A[low.mid]和A[mid+1.. high]的最大子数组,因为这两个子问题仍是最大子数组问题,只是规模更小。
function [max_left,max_right,cross_sum] = FIND_MAX_CROSSONG_SUBARRAY(A,low,mid,high) %UNTITLED2 此处显示有关此函数的摘要 % 此处显示详细说明 left_sum=-1000; sum=0; for i=mid:-1:low sum=sum+A(i); if sum>left_sum left_sum=sum; max_left=i; end end right_sum=-1000; sum=0; for j=(mid+1):1:high sum=sum+A(j); if sum>right_sum right_sum=sum; max_right=j; end end cross_sum=left_sum+right_sum; end
function [loww,highh,third] = FIND_MAXIMUM_SUBARRAY(A,low,high) %UNTITLED3 此处显示有关此函数的摘要 % 此处显示详细说明 left_low=0;left_high=0;right_low=0;right_high=0;cross_low=0;cross_high=0; left_sum=0;right_sum=0;third=0; if high==low loww=low; highh=high; third=A(low) else mid=floor((low+high)/2); [left_low,left_high,left_sum]=FIND_MAXIMUM_SUBARRAY(A,low,mid); [right_low,right_high,right_sum]=FIND_MAXIMUM_SUBARRAY(A,mid+1,high); [cross_low,cross_high,cross_sum]=FIND_MAX_CROSSONG_SUBARRAY(A,low,mid,high); left_sum right_sum cross_sum if left_sum>=right_sum && left_sum>=cross_sum loww=left_low; highh=left_high; third=left_sum else if right_sum>=left_sum && right_sum>=cross_sum loww=right_low; highh=right_high; third=right_sum else loww=cross_low; highh=cross_high; third=cross_sum end end end end
最大子数组中含有跨国中点的讨论,与二分法查找略有不同。
矩阵乘法
A和B为n*n的矩阵。定义乘积C=A*B:
c_{ij}=\sum_{k=1}^{n}{a_{ik}*b_{kj}}
function [C] = SQUARE_MATRIX_MUL(A,B) %UNTITLED4 此处显示有关此函数的摘要 % 此处显示详细说明 n=size(A,1); C=zeros(n,n); if n==1 C(1,1)=A(1,1)*B(1,1); else A11=A(1:(n/2),1:(n/2));A12=A(1:(n/2),(n/2+1):n); A21=A((n/2+1):n,1:n/2);A22=A((n/2+1):n,(n/2+1):n); B11=B(1:(n/2),1:(n/2));B12=B(1:(n/2),(n/2+1):n) B21=B((n/2+1):n,1:n/2);B22=B((n/2+1):n,(n/2+1):n) C11=C(1:(n/2),1:(n/2));C12=C(1:(n/2),(n/2+1):n); C21=C((n/2+1):n,1:n/2);C22=C((n/2+1):n,(n/2+1):n); C11=SQUARE_MATRIX_MUL(A11,B11)+SQUARE_MATRIX_MUL(A12,B21); C12=SQUARE_MATRIX_MUL(A11,B12)+SQUARE_MATRIX_MUL(A12,B22); C21=SQUARE_MATRIX_MUL(A21,B11)+SQUARE_MATRIX_MUL(A22,B21); C22=SQUARE_MATRIX_MUL(A21,B12)+SQUARE_MATRIX_MUL(A22,B22); C=[C11,C12;C21,C22]; end end
关于Strassen方法,请参阅下面的博客。