递归与分治策略讲义
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
O(1) T (n ) = 7T (n / 2) + O n 2
( )
n=2 n>2
20
T(n)=O(nlog7) =O(n2.81)
较大的改进☺ ☺
例三:棋盘覆盖
在一个2k×2k个方格组成的棋盘中,恰有一个方格与其他方 格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。 在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆 盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2 个L型骨牌不得重叠覆盖。 易知,覆盖任意一个2k×2k的特殊棋盘,用到的骨牌数恰好 为(4K-1)/3。
3
递归的概念
例1 阶乘函数 n=0 1 可递归地定义为: n! = n(n − 1)! n > 0 其中: n=0时,n!=1为边界条件 n>0时,n!=n(n-1)!为递归方程 边界条件与递归方程是递归函数的二个要素, 递归函数只有具备了这两个要素,才能在有 限次计算后得出结果。
4
递归的概念
一个分治法将规模为n的 问题分成k个规模为n/m 的子问题去解。设分解 阀值n0=1,且adhoc解规 模为1的问题耗费1个单 位时间。再设将原问题 分解为k个子问题以及用 merge将k个子问题的解 合并为原问题的解需用 f(n)个单位时间。用T(n) 表示该分治法解规模为 |P|=n的问题所需的计算 时间,则有(右上)。 通过迭代法求得方程解 (右下) 。
这条特征涉及到分治法的效率,如果各子问题是不独立 的,则分治法要做许多不必要的工作,重复地解公共的 子问题,此时虽然也可用分治法,但一般用动态规划较 好。
12
例一:大整数乘法问题
用计算机进行两个大的整数相乘,会引起以下 的问题: • 太大的数计算机无法表示,若改用浮点数, 则只能得到近似值; 如何精确地表示和计算两个大的整数相乘 编 程求解 问题: 给定两个n位数的整数N1和N2(不实一般性, 假设n=2k),求value=N1*N2=?
k =1
n
若依此定义来计算A和B的乘积矩阵C,则每 计算C的一个元素C[i][j],需要做n次乘法和 n-1次加法。因此,算出矩阵C的 个元素所需 的计算时间为O(n3)
百度文库18
简单分治法求矩阵乘
首先假定n是2的幂。使用与上例类似的技术,将矩阵A,B和C中每一矩 阵都分块成4个大小相等的子矩阵。由此可将方程C=AB重写为:
public static void hanoi(int n, int a, int b, int c) { if (n > 0) { hanoi(n-1, a, c, b); move(a,b); hanoi(n-1, c, b, a); } }
思考:如果塔的个数变为a,b,c,d四个,现要将n个圆盘从a全部移动到d,移 动规则不变,求移动步数最小的方案。
8
幂运算时间复杂性分析
N(n)= 1 N(n/2)+1 N(n-1)+1 if n=1 if n is even if n is odd
T=O(logn)
9
递归小结
优点:结构清晰,可读性强,而且容易用数学 归纳法来证明算法的正确性,因此它为设计算 法、调试程序带来很大方便。 缺点:递归算法的运行效率较低,无论是耗费 的计算时间还是占用的存储空间都比非递归算 法要多。 解决方法:在递归算法中消除递归调用,使其 转化为非递归算法。
10
分治法的基本思想
分治法的基本思想
分治法的基本思想是将一个规模为n的问题分解为k个规 模较小的子问题,这些子问题互相独立且与原问题相同。 对这k个子问题分别求解。如果子问题的规模仍然不够小, 则再划分为k个子问题,如此递归的进行下去,直到问题 规模足够小,很容易求出其解为止。 将求出的小规模的问题的解合并为一个更大规模的问题 的解,自底向上逐步求出原来问题的解。 分治法的设计思想是,将一个难以直接解决的大问题, 分割成一些规模较小的相同问题,以便各个击破,分而 治之。
5
递归的概念
Hanoi塔问题 设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘,这些圆盘自下 而上,由大到小地叠在一起。各圆盘从小到大编号为1,2,…,n,现要求将塔 座a上的这一叠圆盘移到塔座b上,并仍按同样顺序叠置。在移动圆盘时应 遵守以下移动规则: 1) 每次只能移动1个圆盘; 2) 任何时刻都不允许将较大的圆盘压在较小的圆盘之上; 3) 在满足移动规则1和2的前提下,可将圆盘移至a,b,c中任一塔座上。
C11 C12 A11 C = A 21 C22 21
由此可得:
A12 B11 A22 B21
B12 B22
C11 = A11 B11 + A12 B21
复杂度分析
C12 = A11 B12 + A12 B22
C21 = A21 B11 + A22 B21 C22 = A21 B12 + A22 B22
算法设计与分析
第2章 递归与分治策略
本章主要知识点:
递归的概念 分治法的基本思想 大整数的乘法 二分搜索技术 矩阵乘法 密码学简介
2
递归的概念
直接或间接地调用自身的算法称为递归算法 递归算法。 递归算法 用函数自身给出定义的函数称为递归函数 递归函数。 递归函数 在计算机算法设计与分析中,使用递归技术 往往使函数的定义和算法的描述简洁且易于 理解。 下面来看几个实例。
O(1) n =1 T(n) = kT(n / m) + f (n) n > 1
T (n) = n
logm k
+
logm n−1 −1 j =0
k j f (n / m j ) ∑
17
例二:Strassen矩阵乘法
n×n矩阵A和B的乘积矩阵C中的元素C[i,j]定 义为:
cij = ∑ aik bkj
14
大整数乘法算法描述
Integer dc(N1,N2,N)
{ if ( N <= 2) return N1*N2; //N是整数的位数值 w=left(N1,N/2); x=right(N1,N/2); y=left(N3,N/2); z=right(N3,N/2); wy=dc(w, y, N/2); xz=dc(x, z, N/2); r=dc(w+x, y+z, N/2); MULTI= 10N/2wy+10N/4(r-wy-xz)+xz; //将各子问题的解合 并为原问题的解 return MULTI; }
13
大整数乘法的分治算法思想
分 一分为二、二分为四(以n=4为例) N1=102w+x,N2=102y+z,于是: N1*N2=(102w+x)*(102*y+z) =104wy+102(wz+xy)+xz 因为r=(w+x)(y+z)=wy+(wy+xy)+xz 求出wy 、xz、和r= (w+x)(y+z) 治 合并:求和。 效率:四次n/2位的乘法,减为三次n/2的乘法。
21
分治策略求解
当k>0时,将2k×2k棋盘分割为4个2k-1×2k-1 子棋盘(a)所示。 特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无 特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋 盘,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如 (b)所示,从而将原问题转化为4个较小规模的棋盘覆盖问题。 递归地使用这种分割,直至棋盘简化为棋盘1×1。
11
分治法的基本思想
分治法的适用条件
分治法所能解决的问题一般具有以下几个特征:
• • • • 该问题的规模缩小到一定的程度就可以容易地解决; 该问题可以分解为若干个规模较小的相同问题,即该问题具有 最优子结构性质 利用该问题分解出的子问题的解可以合并为该问题的解; 该问题所分解出的各个子问题是相互独立的,即子问题之间不 包含公共的子问题。
1 n=0 1 F(n) = n=1 F(n−1) +F(n−2) n>1
例2 Fibonacci数列 无穷数列1,1,2,3,5,8,13,21, 34,55,…,被称为Fibonacci数列。它 可以递归地定义为: 第n个Fibonacci数可递归地计算如下:
Algorithm: fibonacci(int n) Input: integer n; Output: the number of n-th item of Fibonacci 1. if (n <= 1) return 1; 2. return fibonacci(n-1)+fibonacci(n-2);
22
算法描述
void chessBoard(int tr,tc,dr,dc,size) { if (size == 1) return; int t = tile++; // L型骨牌号 s = size/2; // 分割棋盘 // 覆盖左上角子棋盘 if (dr < tr + s && dc < tc + s) // 特殊方格在此棋盘中 chessBoard(tr, tc, dr, dc, s); else {// 此棋盘中无特殊方格 // 用 t 号L型骨牌覆盖右下角 board[tr + s - 1][tc + s - 1] = t; // 覆盖其余方格 CB(tr, tc, tr+s-1, tc+s-1, s);} // 覆盖右上角子棋盘 if (dr < tr + s && dc >= tc + s) // 特殊方格在此棋盘中 CB(tr, tc+s, dr, dc, s); else {// 此棋盘中无特殊方格 // 用 t 号L型骨牌覆盖左下角 board[tr + s - 1][tc + s] = t; // 覆盖其余方格 CB(tr,tc+s,tr+s-1,tc+s, s);}
人们从大量实践中发现,在用分治法设计算法时, 最好使子问题的规模大致相同。即将一个问题分成 大小相等的k个子问题的处理方法是行之有效的。 这种使子问题规模大致相等的做法是出自一种平衡 (balancing)子问题的思想,它几乎总是比子问题规 模不等的做法要好。
16
分治法的时间复杂性
分治法的复杂性分析
O(1) T (n ) = 8T (n / 2 ) + O n 2
( )
n=2 n>2
19
T(n)=O(n3) 没有改进
改进算法
为了降低时间复杂度,必须减少乘法的次数。而其关键在于计算2个2阶 方阵的乘积时所用乘法次数能否少于8次。为此,Strassen提出了一种只 用7次乘法运算计算2阶方阵乘积的方法(但增加了加/减法次数): M1=A11(B12-B22) M2=(A11+A12)B22 M3=(A21+A22)B11 M4=A22(B21-B11) M5=(A11+A22)(B11+B22) M6=(A12-A22)(B21+B22) M7=(A11-A21)(B11+B12) 做了这7次乘法后,在做若干次加/减法就可以得到: C11=M5+M4-M2+M6 C12=M1+M2 C21=M3+M4 C22=M5+M1-M3-M7 复杂度分析
6
例四:幂运算
求x=ax=? Exposeq(a, n) { r a; for i 1 to n-1 do r return r; }
a*r;
7
幂运算改进算法
求x=an=? 如果n=1, 则an=a 如果n是偶数,则an=(an/2 ) 2 ,否则an =a*an-1 ExpoDC(a, n) { if n = 1 then return a; if n 是偶数 then return (ExpoDC(a, n/2))2 ; return a*Exposeq(a,n-1); }
// 覆盖左下角子棋盘 if (dr >= tr + s && dc < tc + s) // 特殊方格在此棋盘中 CB(tr+s, tc, dr, dc, s); else {// 用 t 号L型骨牌覆盖右上角 board[tr + s][tc + s - 1] = t; // 覆盖其余方格 CB(tr+s, tc, tr+s, tc+s-1, s);} // 覆盖右下角子棋盘 if (dr >= tr + s && dc >= tc + s) // 特殊方格在此棋盘中 CB(tr+s, tc+s, dr, dc, s); else {// 用 t 号L型骨牌覆盖左上角 board[tr + s][tc + s] = t; // 覆盖其余方格 CB(tr+s, tc+s, tr+s, tc+s, s);} }
15
分治法的通用模板
分治法的基本步骤
divide-and-conquer(P)
{ if ( | P | <= n0) adhoc(P); //即如果|P|足够小或简单,便解决之。 divide P into smaller subinstances P1,P2,...,Pk;//分解问题 for (i=1,i<=k,i++) yi=divide-and-conquer(Pi); //递归的解各子问题 return merge(y1,...,yk); //将各子问题的解合并为原问题的解 }