<o:p> </o:p>
1、分治法的基本思想<o:p></o:p>
任何一个可以用计算机求解的问题所需的计算时间都与其规模N有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算;n=2时,只要作一次比较即可排好序;n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的。<o:p></o:p>
分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。<o:p></o:p>
如果原问题可分割成k个子问题(1<k≤n),且这些子问题都可解,并可利用这些子问题的解求出原问题的解,那么这种分治法就是可行的。由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。这自然导致递归过程的产生。分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。<o:p></o:p>
2、分治法的适用条件<o:p></o:p>
分治法所能解决的问题一般具有以下几个特征:<o:p></o:p>
(1)该问题的规模缩小到一定的程度就可以容易地解决;<o:p></o:p>
(2)该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;<o:p></o:p>
(3)利用该问题分解出的子问题的解可以合并为该问题的解;<o:p></o:p>
(4)该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。<o:p></o:p>
上述的第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;第二条特征是应用分治法的前提,它也是大多数问题可以满足的,此特征反映了递归思想的应用;第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑贪心法或动态规划法。第四条特征涉及到分治法的效率,如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。<o:p></o:p>
3、分治法的基本步骤<o:p></o:p>
分治法在每一层递归上都有三个步骤:<o:p></o:p>
(1)分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;<o:p></o:p>
(2)解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;<o:p></o:p>
(3)合并:将各个子问题的解合并为原问题的解。<o:p></o:p>
它的一般的算法设计模式如下:<o:p></o:p>
Divide_and_Conquer(P)<o:p></o:p>
if |P|≤n0<o:p></o:p>
then return(ADHOC(P))<o:p></o:p>
将P分解为较小的子问题P1、P2、…、Pk<o:p></o:p>
for i←1 to k<o:p></o:p>
do<o:p></o:p>
yi ← Divide-and-Conquer(Pi) △ 递归解决Pi<o:p></o:p>
T ← MERGE(y1,y2,…,yk) △ 合并子问题<o:p></o:p>
Return(T)<o:p></o:p>
其中 |P| 表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。ADHOC(P)是该分治法中的基本子算法,用于直接解小规模的问题P。因此,当P的规模不超过n0时,直接用算法ADHOC(P)求解。<o:p></o:p>
算法MERGE(y1,y2,…,yk)是该分治法中的合并子算法,用于将P的子问题P1、P2、…、Pk的相应的解y1、y2、…、yk合并为P的解。<o:p></o:p>
根据分治法的分割原则,原问题应该分为多少个子问题才较适宜?各个子问题的规模应该怎样才为适当?这些问题很难予以肯定的回答。但人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模大致相同。换句话说,将一个问题分成大小相等的k个子问题的处理方法是行之有效的。许多问题可以取k=2。这种使子问题规模大致相等的做法是出自一种平衡子问题的思想,它几乎总是比子问题规模不等的做法要好。<o:p></o:p>
分治法的合并步骤是算法的关键所在。有些问题的合并方法比较明显,有些问题合并方法比较复杂,或者是有多种合并方案;或者是合并方案不明显。究竟应该怎样合并,没有统一的模式,需要具体问题具体分析。<o:p></o:p>
【问题】 大整数乘法<o:p></o:p>
问题描述:<o:p></o:p>
通常,在分析一个算法的计算复杂性时,都将加法和乘法运算当作是基本运算来处理,即将执行一次加法或乘法运算所需的计算时间当作一个仅取决于计算机硬件处理速度的常数。<o:p></o:p>
这个假定仅在计算机硬件能对参加运算的整数直接表示和处理时才是合理的。然而,在某些情况下,我们要处理很大的整数,它无法在计算机硬件能直接表示的范围内进行处理。若用浮点数来表示它,则只能近似地表示它的大小,计算结果中的有效数字也受到限制。若要精确地表示大整数并在计算结果中要求精确地得到所有位数上的数字,就必须用软件的方法来实现大整数的算术运算。<o:p></o:p>
请设计一个有效的算法,可以进行两个n位大整数的乘法运算。<o:p></o:p>
设X和Y都是n位的二进制整数,现在要计算它们的乘积XY。我们可以用小学所学的方法来设计一个计算乘积XY的算法,但是这样做计算步骤太多,显得效率较低。如果将每2个1位数的乘法或加法看作一步运算,那么这种方法要作O(n2)步运算才能求出乘积XY。下面我们用分治法来设计一个更有效的大整数乘积算法。<o:p></o:p>
<o:p> </o:p>
图6-3 大整数X和Y的分段<o:p></o:p>
我们将n位的二进制整数X和Y各分为2段,每段的长为n/2位(为简单起见,假设n是2的幂),如图6-3所示。<o:p></o:p>
由此,X=A2n/2+B,Y=C2n/2+D。这样,X和Y的乘积为:<o:p></o:p>
XY=(A2n/2+B)(C2n/2+D)=AC2n+(AD+CB)2n/2+BD (1)<o:p></o:p>
如果按式(1)计算XY,则我们必须进行4次n/2位整数的乘法(AC,AD,BC和BD),以及3次不超过n位的整数加法(分别对应于式(1)中的加号),此外还要做2次移位(分别对应于式(1)中乘2n和乘2n/2)。所有这些加法和移位共用O(n)步运算。设T(n)是2个n位整数相乘所需的运算总数,则由式(1),我们有:<o:p></o:p>
(2)<o:p></o:p>
由此可得T(n)=O(n2)。因此,用(1)式来计算X和Y的乘积并不比小学生的方法更有效。要想改进算法的计算复杂性,必须减少乘法次数。为此我们把XY写成另一种形式:<o:p></o:p>
XY=AC2n+[(A-B)(D-C)+AC+BD]2n/2+BD (3)<o:p></o:p>
虽然,式(3)看起来比式(1)复杂些,但它仅需做3次n/2位整数的乘法(AC,BD和(A-B)(D-C)),6次加、减法和2次移位。由此可得:<o:p></o:p>
(4)<o:p></o:p>
用解递归方程的套用公式法马上可得其解为T(n)=O(nlog3)=O(n1.59)。利用式(3),并考虑到X和Y的符号对结果的影响,我们给出大整数相乘的完整算法MULT如下:<o:p></o:p>
function MULT(X,Y,n); {X和Y为2个小于2n的整数,返回结果为X和Y的乘积XY}<o:p></o:p>
begin<o:p></o:p>
S=SIGN(X)*SIGN(Y); {S为X和Y的符号乘积}<o:p></o:p>
X=ABS(X);<o:p></o:p>
Y=ABS(Y); {X和Y分别取绝对值}<o:p></o:p>
if n=1 then<o:p></o:p>
if (X=1)and(Y=1) then return(S)<o:p></o:p>
else return(0)<o:p></o:p>
else begin<o:p></o:p>
A=X的左边n/2位;<o:p></o:p>
B=X的右边n/2位;<o:p></o:p>
C=Y的左边n/2位;<o:p></o:p>
D=Y的右边n/2位;<o:p></o:p>
ml=MULT(A,C,n/2);<o:p></o:p>
m2=MULT(A-B,D-C,n/2);<o:p></o:p>
m3=MULT(B,D,n/2);<o:p></o:p>
S=S*(m1*2n+(m1+m2+m3)*2n/2+m3);<o:p></o:p>
return(S);<o:p></o:p>
end;<o:p></o:p>
end;<o:p></o:p>
上述二进制大整数乘法同样可应用于十进制大整数的乘法以提高乘法的效率减少乘法次数。<o:p></o:p>
【问题】 最接近点对问题<o:p></o:p>
问题描述:<o:p></o:p>
在应用中,常用诸如点、圆等简单的几何对象代表现实世界中的实体。在涉及这些几何对象的问题中,常需要了解其邻域中其他几何对象的信息。例如,在空中交通控制问题中,若将飞机作为空间中移动的一个点来看待,则具有最大碰撞危险的2架飞机,就是这个空间中最接近的一对点。这类问题是计算几何学中研究的基本问题之一。下面我们着重考虑平面上的最接近点对问题。<o:p></o:p>
最接近点对问题的提法是:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。<o:p></o:p>
严格地说,最接近点对可能多于1对。为了简单起见,这里只限于找其中的一对。<o:p></o:p>
这个问题很容易理解,似乎也不难解决。我们只要将每一点与其他n-1个点的距离算出,找出达到最小距离的两个点即可。然而,这样做效率太低,需要O(n2)的计算时间。我们能否找到问题的一个O (nlogn)算法。<o:p></o:p>
这个问题显然满足分治法的第一个和第二个适用条件,我们考虑将所给的平面上n个点的集合S分成2个子集S1和S2,每个子集中约有n/2个点,然后在每个子集中递归地求其最接近的点对。在这里,一个关键的问题是如何实现分治法中的合并步骤,即由S1和S2的最接近点对,如何求得原集合S中的最接近点对,因为S1和S2的最接近点对未必就是S的最接近点对。如果组成S的最接近点对的2个点都在S1中或都在S2中,则问题很容易解决。但是,如果这2个点分别在S1和S2中,则对于S1中任一点p,S2中最多只有n/2个点与它构成最接近点对的候选者,仍需做n2/4次计算和比较才能确定S的最接近点对。因此,依此思路,合并步骤耗时为O(n2)。整个算法所需计算时间T(n)应满足:<o:p></o:p>
T(n)=2T(n/2)+O(n2)<o:p></o:p>
它的解为T(n)=O(n2),即与合并步骤的耗时同阶,显示不出比用穷举的方法好。从解递归方程的套用公式法,我们看到问题出在合并步骤耗时太多。这启发我们把注意力放在合并步骤上。<o:p></o:p>
为了使问题易于理解和分析,我们先来考虑一维的情形。此时S中的n个点退化为x轴上的n个实数x1、x2、…、xn。最接近点对即为这n个实数中相差最小的2个实数。我们显然可以先将x1、x2、…、xn排好序,然后,用一次线性扫描就可以找出最接近点对。这种方法主要计算时间花在排序上,因此如在排序算法中所证明的,耗时为O(nlogn)。然而这种方法无法直接推广到二维的情形。因此,对这种一维的简单情形,我们还是尝试用分治法来求解,并希望能推广到二维的情形。<o:p></o:p>
假设我们用x轴上某个点m将S划分为2个子集S1和S2,使得S1={x∈S | x≤m};S2={x∈S | x>m}。这样一来,对于所有p∈S1和q∈S2有p<q。<o:p></o:p>
递归地在S1和S2上找出其最接近点对{p1,p2}和{q1,q2},并设δ=min{|p1-p2|,|q1-q2|},S中的最接近点对或者是{p1,p2},或者是{q1,q2},或者是某个{p3,q3},其中p3∈S1且q3∈S2。如图1所示。<o:p></o:p>
<o:p> </o:p>
图1 一维情形的分治法<o:p></o:p>
我们注意到,如果S的最接近点对是{p3,q3},即 | p3-q3 | < δ,则p3和q3两者与m的距离不超过δ,即 | p3-m | < δ,| q3-m | < δ,也就是说,p3∈(m-δ,m),q3∈(m,m+δ)。由于在S1中,每个长度为δ的半闭区间至多包含一个点(否则必有两点距离小于δ),并且m是S1和S2的分割点,因此(m-δ,m)中至多包含S中的一个点。同理,(m,m+δ)中也至多包含S中的一个点。由图1可以看出,如果(m-δ,m)中有S中的点,则此点就是S1中最大点。同理,如果(m,m+δ)中有S中的点,则此点就是S2中最小点。因此,我们用线性时间就能找到区间(m-δ,m)和(m,m+δ)中所有点,即p3和q3。从而我们用线性时间就可以将S1的解和S2的解合并成为S的解。也就是说,按这种分治策略,合并步可在O(n)时间内完成。这样是否就可以得到一个有效的算法了呢?<o:p></o:p>
还有一个问题需要认真考虑,即分割点m的选取,及S1和S2的划分。选取分割点m的一个基本要求是由此导出集合S的一个线性分割,即S=S1∪S2 ,S1∩S2=Φ,且S1 {x | x≤m};S2 {x | x>m}。容易看出,如果选取m=[max(S)+min(S)]/2,可以满足线性分割的要求。选取分割点后,再用O(n)时间即可将S划分成S1={x∈S | x≤m}和S2={x∈S | x>m}。然而,这样选取分割点m,有可能造成划分出的子集S1和S2的不平衡。例如在最坏情况下,|S1|=1,|S2|=n-1,由此产生的分治法在最坏情况下所需的计算时间T(n)应满足递归方程:<o:p></o:p>
T(n)=T(n-1)+O(n)<o:p></o:p>
它的解是T(n)=O(n2)。这种效率降低的现象可以通过分治法中“平衡子问题”的方法加以解决。也就是说,我们可以通过适当选择分割点m,使S1和S2中有大致相等个数的点。自然地,我们会想到用S的n个点的坐标的中位数来作分割点。在选择算法中介绍的选取中位数的线性时间算法使我们可以在O(n)时间内确定一个平衡的分割点m。<o:p></o:p>
至此,我们可以设计出一个求一维点集S中最接近点对的距离的算法pair如下。<o:p></o:p>
Float pair(S);<o:p></o:p>
{ if | S | =2 δ= | x[2]-x[1] | /*x[1..n]存放的是S中n个点的坐标*/<o:p></o:p>
else<o:p></o:p>
{ if ( | S | =1) δ=∞<o:p></o:p>
else<o:p></o:p>
{ m=S中各点的坐标值的中位数;<o:p></o:p>
构造S1和S2,使S1={x∈S | x≤m},S2={x∈S | x>m};<o:p></o:p>
δ1=pair(S1);<o:p></o:p>
δ2=pair(S2);<o:p></o:p>
p=max(S1);<o:p></o:p>
q=min(S2);<o:p></o:p>
δ=min(δ1,δ2,q-p);<o:p></o:p>
}<o:p></o:p>
return(δ);<o:p></o:p>
}<o:p></o:p>
由以上的分析可知,该算法的分割步
分享到:
相关推荐
本资源“常用算法分析设计”深入探讨了多种重要的算法设计方法,旨在帮助开发者和学习者提升解决复杂问题的能力。 首先,我们来讨论分治策略。分治法是一种将大问题分解为小问题来解决的思路,它将复杂的问题划分为...
分治法是一种重要的算法设计策略,它通过将大问题分解为规模较小的相似子问题来解决。这种方法适用于那些可以通过解决小规模问题并合并结果来解决大规模问题的场合。以下是分治法的详细解释: 1. **基本思想**: ...
常用的算法设计技术有迭代法、穷举搜索法、递推法、贪婪法、回溯法、分治法和动态规划法等。另外,为了以更简洁的形式设计和描述算法,在设计算法时常采用递归技术,用递归描述算法。 本讲中,主要介绍分治法。
"算法设计与分析之分治法" 在算法设计与分析中,分治法是一种非常重要的算法设计技术。它通过将复杂的问题分解成更小的子问题,然后递归地解决这些子问题,以达到解决原始问题的目的。下面,我们将通过四个小实验来...
### 常用算法设计方法概述 #### 一、算法的重要性 在计算机科学领域,算法是一种解决问题的具体步骤或指令集,它定义了处理任务的方法。一个有效的算法能够确保计算机能够高效且准确地解决各种问题。设计算法是软件...
分治法是一种常见的算法设计策略,其核心思想是将一个难以直接解决的大问题划分成若干个规模较小的同类问题,递归地解决这些子问题,然后再将子问题的解合并以解决原问题。这种策略在很多算法中都得到了广泛的应用,...
分治算法是五大常用算法之一,它是一种很重要的算法设计策略,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或多个相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接...
【软考常用算法设计方法详解】 在信息技术领域,软件设计师经常需要解决各种复杂的问题,而算法设计是解决问题的关键。算法是一系列明确的指令,用于解决特定问题或执行特定任务。在软考(全国计算机技术与软件专业...
分治法是一种常用的算法设计方法,它通过将问题分解成若干个子问题,然后求解子问题,由此得到原问题的解。分治法的基本思想是“分而治之”,即将问题分解成多个子问题,然后将子问题的解合并为原问题的解。 分治法...
然而,随着问题规模的增长,更高级的算法设计方法如递推法、贪婪法、回溯法、分治法、动态规划法等变得更为重要,因为它们能够以更高效的方式解决复杂问题。在设计算法时,除了正确性和效率外,还要考虑算法的可读性...
"算法设计与实现-分治法" 本知识点总结了算法设计与实现中的分治法,涵盖了算法概要、效率概述、折半查找、合并排序、快速排序、大整数排序、Strassen 矩阵乘法等多种算法思想和实现过程。同时,也提供了相关习题供...
这篇文档“常用算法设计方法”提供了一份详尽的指南,涵盖了多种广泛应用于计算机科学领域的算法。以下是对这些算法的详细介绍: 1. **排序算法**:排序是数据处理的基础,包括冒泡排序、插入排序、选择排序、快速...
2. Divide and Conquer:分治法是一种常用的算法设计方法,通过将原问题划分成更小的子问题,然后递归地解决子问题,最后合并子问题的解来获得原问题的解。 3. Recursive Algorithm:递归算法是一种常用的算法设计...
计算机二分法的算法步骤-五大常用算法之一:分治算法,算法数据结构 五大常用算法 分治算法是一种常用的算法设计方法,它将一个规模为n的问题P分解成k个规模较小的子问题,这些子问题相互独立,并且与原来的问题...
"常用算法设计方法(word版)"为初学者和算法设计爱好者提供了一个系统的算法设计方法概述,涵盖了迭代法、穷举搜索法、递推法、贪婪法、回溯法、分治法、动态规划法等多种常用的算法设计方法,并提供了 C 语言编写...