第二章 递归与分治策略
第2章 递归与分治(1)
本例若设p(n)为正整数n的划分数,则难以找到递 归关系。
17
现考虑增加一个自变量:将最大加数n1不大于m的划 分个数记作q(n,m)。q(n,m)有如下递归关系:
(1) q(n,1)=1,n1; 当最大加数n1不大于1时,任何正整数n只有一种划分形式, 即
• (8)循环赛日程表。
2
算法总体思想
对这k个子问题分别求解。如果子问题的规模仍然不够 将小要,求则解再的划较分大为k规个模子的问问题题,分如割此成递k归个的更进小行规下模去的,子直问
题到。问题规模足够小,很容易求出其解为止。
T(n)
=n
T(n/2)
T(n/2)
T(n/2)
T(n/2)
3
算法总体思想
以想象的慢速度趋向正无穷大。
13
例4:排列问题
排设列计。一个递归算法生成n个元素{r1,r2,…,rn}的全 ➢设R={r1,r2,…,rn}是要进行排列的n个元素,Ri=R{ri}。 ➢集合X中元素的全排列记为perm(X)。 (permutation) ➢(ri)perm(X)表示在全排列perm(X)的每一个排列前 加上前缀得到的排列。
22
➢由此可见,n个圆盘的移动问题可分为2次n-1个圆 盘的移动问题,这又可以递归地用上述方法来做。
解Hanoi塔问题的递归算法如下:
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); } }
n1,m1 nm nm
计算机专业课《算法》_第二章 递归与分治策略
“Hanoi 塔”问题演示 a 初始 a 步骤1 a
c
b
c
“Hanoi 塔”问题程序
void hanoi(int n,a,b,c)
{ if n == 1 move( 1, a, b );
else { hanoi( n-1, a, c, b );
move(n, a, b ); hanoi( n-1, c,b, a) ;
• 递归优点:结构清晰,可读性强
• 递归缺点:递归算法的运行效率较低,无论是耗 费的计算时间还是占用的存储空间都比非递归算 法要多。
整数划分问题的递归关系q(n,m)
如设p(n)为正整数n的划分数,则难以找到递归关系 • q(n,m):正整数n的不同的划分中,最大加数不 大于m的划分个数个数 q(n,m)=
1 q(n,n) 1+q(n,n-1) q(n,m-1)+q(n-m,m) n=1, m=1 n<m n=m n>m>1
递归函数举例(5)
学习要点
理解递归的概念。 掌握设计有效算法的分治策略。
通过典型范例,学习分治策略设计技巧。
2.1 递归的概念
• 递归算法:一个直接或间接地调用自身的算法 • 递归方程:对于递归算法,一般可把时间代 价表示为一个递归方程 • 递归函数:使用函数自身给出定义的函数 • 解递归方程最常用的方法是进行递归扩展
递归函数举例(1)
• 阶乘函数 n !=
1 n(n-1)! n=1 n>1
• Fibonacci数列
1 n=0
F(n)=
1 F(n-1)+F(n-2)
n=1 n>1
初始条件与递归方程是递归函数的二个要素
第二章递归与分治
正整数的划分
在正整数的所有不同划分中,将最大加数n1不 大于m的划分个数记为q(n, m),可以建立如下 的递归关系: 的二元递归函数: n= 1 n, 或m m =1 q (n, m) { 1 (1) q(n, 1)=1, q(1, m) 最简单情形: =1 ≥1; q(n, m) = 1 + q(n, n –1) n ≤ m if (n < 1) || (m < 1) return 0; 递归关系: (2) q(n, n) = 1 + q(n, n–1),n>1; q(n, m– 1)+q(n –m, m) if (n == 1) || (m == 1) return 1; n>m>1 产生的新情况: if (n == 1) || (n < m) return 1 + q(n, n–1); return q(n, m –1) q(n m, } n>m>1 (3) q(n, m) =q (n, m+ –1) +–q (nm); –m, m), (4) q(n, m) = q(n, n),=n < mn) 。 整数 n的划分数 ρ(n) q (n, 。
2019/3/24 计算机算法设计与分析 3
Hanoi塔问题
例 Hanoi 1:Hanoi 塔的解可以很自然地看成这样一个过程: 塔问题:有A、B、C三根柱子。A
上有n个圆盘,自下而上由大到小地叠在一起。 于是可得到如下的程序: (1)先将A上面 现要将 A上的全部圆 n –1 个盘移至 C。 void Hanoi(int n, int Fr, int To, int As) { 盘移到B上,并要求: if (n > 0) { (2)再将 A上剩下 (1)每次只能移动一个 Hanoi(n–1, Fro, Ass, To); 的1 个盘移至 B。 圆盘; (2)任何时刻都 B C A Move(Fro, To); 不允许将较大的圆盘 Hanoi(n–1, Ass, To, Fro)} (3) 最后将C上的 压在较小的圆盘上; } n–(3) 1个盘移至 B。A、B、 圆盘只能在 C三个柱子间移动。
c02 递归与分治策略分解
T(n)
=n
n/2
n/2
n/2
n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
算法总体思想
将求出的小规模的问题的解合并为一个更大规 模的问题的解,自底向上逐步求出原来问题的 解。
{ if (n <= 1) return 1; return fibonacci(n-1)+fibonacci(n-2);
T(n)
=n
n/2
n/2
n/2
n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
算法总体思想
将求出的小规模的问题的解合并为一个更大规 模的问题的解,自底向上逐步求出原来问题的 解。
第2章 递归与分治策略
学习要点: 理解递归的概念。 掌握设计有效算法的分治策略。 通过下面的范例学习分治策略设计技巧。
1)二分搜索技术; (2)大整数乘法; 3)Strassen矩阵乘法; 4)棋盘覆盖; 5)合并排序和快速排序; 6)线性时间选择; (7)最接近点对问题; (8)循环赛日程表。
算法总体思想
2.1 递归的概念
直接或间接地调用自身的算法称为递归算法。 用函数自身给出定义的函数称为递归函数。
由分治法产生的子问题是原问题的较小模式, 为使用递归技术提供了方便。反复应用分治 手段,使子问题与原问题类型一致而其规模 却不断缩小,最终使子问题缩小到很容易直 接求出其解。这自然导致递归过程的产生。
第2章 递归与分治策略
前2例中的函数都可以找到相应的非递归方式定义: 例1 阶乘函数
F ( n ) n ! 1 2 3 ( n 1 ) n
例2 Fibonacci(斐波那齐)数列
F(n)15125n1125n1
M O V E (X , 4 , Z ) H A N O I(3 , Y , X , Z )
H A N O I(2 , Y , Z , X ) H A N O I(1 , Y , X , Z ) M O V E (Y , 1 , Z ) M O V E (Y , 2 , X ) H A N O I(1 , Z , Y , X ) M O V E (Z , 1 , X ) M O V E (Y , 3 , Z )
例1 阶乘函数 阶乘函数可递归地定义为:
n!n(n11)!
n0 n0
边界条件 递归方程
边界条件与递归方程是递归函数的二个要素,递归函数 只有具备了这两个要素,才能在有限次计算后得出结果。
int factorial(int n) { if (n== 0) return 1; return n*factorial( n-1); }
F(6)
F(5)
F(4)
F(4)
Hale Waihona Puke F(3)F(3)F(3)
F(2)
F(2) F(1)
F(2) F(1) F(1)
F(2)
F(1) F(1) F(0) F(1) F(0) F(1) F(0)
F(1) F(0)
斐波那齐数列算法的递归结构(n=5)
求斐波那齐数列的非递归程序:
void fibonacci (int n) {
第2章 递归与分治策略优秀课件
以Rk( c ) 记放k个车的不同放法,令R0( c ) = 1 上面的棋盘C的棋盘多项式等于:
R( c )=1+n2x+
(
n
2
)2x2
+……+
(
n
r
)2
+n!xn
但是,我们经常碰到的是不完整的棋盘,这类棋盘 称残缺棋盘。
R( )= 1+X
R(
R ( ) = R ( ) = 1+2X R(
1 2 3 45
a
b c
d
e
(1+3X+X2) (1+4X+3X2)
正交的相乘
R(C)= (1+3X+X2)*(1+4X+3X2) = 1+7X+16X2+13X3+3X4
对照得到: B(0)= 5!- 7*4!+16*3!-13*2!+3*1!+ 0
= 25
这是简单的棋盘多项式能解决的问题,如果 有不正交的情况,我们主要采用下面的方法 进行构造。
) = 1+3X+X2 ) = 1+2X+X2 = (1+X)2
R ( ) = 1+4X+2X2
R(
) = 1+6X+7X2+X3
下面我们看一些应用: example: 在a,b,c,d,e的全排列中,要求a不出现在 第1和第5位,b不出现在第2和第3位,c不出现在 第3和第4位,e不出现在第5位,问:满足这些要 求的全排列有多少?
设所有元素互不相同。在这种情况下,找 出的基准x至少比3(n-5)/10个元素大,因为在 每一组中有2个元素小于本组的中位数,而 n/5个中位数中又有(n-5)/10个小于基准x。同 理,基准x也至少比3(n-5)/10个元素小。而当 n≥75时,3(n-5)/10≥n/4所以按此基准划分所 得的2个子数组的长度都至少缩短1/4。
第2章递归与分治策略
说明:边界条件与递归方程是递归函数的两个要素, 递归函数只有具备了这两个要素,才能在有限次计
算后得出结果。
2024/7/30
算法设计与分析
4
递归函数的内部执行过程
递归函数内部执行过程如下:
(1)运行开始时,为递归调用建立一个工作栈,其结 构包括实参、局部变量和返回地址;
(2)每次执行递归调用之前,把递归函数的实参和局 部变量的当前值以及调用后的返回地址压栈;
• Ackerman函数A(n, m)定义如下,n, m是两个独 立的整变量,其中n, m均≥0:
A1,0 2
A0, m 1
An,0 n 2
An, m AAn 1, m, m 1
m0 n2 n, m 1
2024/7/30
算法设计与分析
8
分析
• A(n,m)的自变量m的每一个值都定义了一个单 变量函数:
– 将求出的小规模的问题的解合并为一个更大规模的问 题的解,自底向上逐步求出原来问题的解。
2024/7/30
算法设计与分析
25
原问题 的规模是n
子问题1 的规模是n/2
子问题2 的规模是n/2
子问题1的解
子问题2的解
原问题的解
2024/7/30
算法设计与分析
26
问题(N个输入)
合 子问题1 并 解
(3)每次递归调用结束后,将栈顶元素出栈,使相应 的实参和局部变量恢复为调用前的值,然后转向返回地 址指定的位置继续执行
举例2-2:Fibonacci数列
• 无穷数列1, 1, 2, 3, 5, 8, 13, 21, 34, 55,…,被称
为Fibonacci数列。 • 它可以递归定义为:
2、递归与分治策略
例4
Hanoi塔问题 Hanoi塔问题
在问题规模较大时,较难找到一般的方法,因此我们尝试 用递归技术来解决这个问题。 当n=1时,问题比较简单。此时,只要将编号为1的圆盘从 塔座a直接移至塔座b上即可。 当n>1时,需要利用塔座c作为辅助塔座。此时若能设法 将n-1个较小的圆盘依照移动规则从塔座a移至塔座c,然 后,将剩下的最大圆盘从塔座a移至塔座b,最后,再设法 将n-1个较小的圆盘依照移动规则从塔座c移至塔座b。 由此可见,n个圆盘的移动问题可分为2次n-1个圆盘的移 动问题,这又可以递归地用上述方法来做。由此可以设计 出解Hanoi塔问题的递归算法如下。
将求出的小规模的问题的解合并为一个更大规模的问题 的解,自底向上逐步求出原来问题的解。
T(n)
n/2
=
n
n/2
n/2
n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4) T(n/4)T(n/4) T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)
第n个Fibonacci数可递归地计算如下: public static int fibonacci(int n) { if (n <= 1) return 1; return fibonacci(n-1)+fibonacci(n-2); }
前2例中的函数都可以找到相应的非递归方式定义:
第二章递归与分治策略
例: Fibonacci数列
1
n 0,1
F(n)= F(n 1) F(n 2) n 1
求第n个Fibonacci数的算法描述
public static int fibonacci(int n)
{
if(n<=1)return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
注: 1. perm(list,k,m)产生前缀是list[0:k-1],后缀是list[k:m]的全 排序的所有排序。
2.调用perm(list,0,n-1)产生list[0:n-1]的全排序
3. 算法将list[k:m]中每一个元素分别与list[k]中的元素交换, 递归产生list[k+1:m]的全排序,结果为list[0:k]的后缀。
例 Hanoi塔 a,b,c三个塔,a中有n个圆盘(由小到大叠放),从小到大编
号为1,2,…,n 要求:将塔a上的盘移到塔c上,按原顺序叠放 规则:(1)每次只移一个圆盘
(2)不允许大盘压在小盘上 public static void hanoi(int n,int a,int b,int c) {
2.1 递归的概念
❖ 递归调用:直接或间接地调用自身的算法。
❖ 递归函数:用函数自身给出定义的函数。
❖ 递归的优缺点:
优点:易于实现,便于分析。
不足:占用空间较多,在某些设计中可能 会导致重复计算。
❖ 递归调用的一般条件:一般问题本身应具有递 归特性,应具备递归结束的条件。
例:阶乘函数
1 n0
}
例 :排列问题
R={r1,r2,···,rn}进行排列
Ri=R-{ri} perm(X)表示集合X的全排序,(ri)perm(X)是X全排序前加
算法第二章递归与分治策略小结
算法第⼆章递归与分治策略⼩结第2章递归与分治策略2.1.递归的概念递归算法:直接或间接地调⽤⾃⾝的算法递归函数:⽤函数⾃⾝给出定义的函数递归函数的第⼀句⼀定是if语句作为边界条件,然后就是递归⽅程如:阶乘函数的第⼀句就是if条件语句1int factorial(int n){2if( n ==0)3return1;4return n*factorial(n-1);5 }※※※递归函数中⽐较著名的是Hanoi塔问题Hanoi塔问题。
设a,b,c是3个塔座。
开始时,在塔座a上有⼀叠共n个圆盘,这些圆盘⾃下⽽上,由⼤到⼩地叠在⼀起。
各圆盘从⼩到⼤编号为1,2,…,n,现要求将塔座a上的这⼀叠圆盘移到塔座c上,并仍按同样顺序叠置。
在移动圆盘时应遵守以下移动规则:规则1:每次只能移动1个圆盘;规则2:任何时刻都不允许将较⼤的圆盘压在较⼩的圆盘之上;规则3:在满⾜规则1和2的前提下,可将圆盘移⾄a,b,c中任⼀塔座上。
hanoi塔问题题⽬描述1 #include<iostream>2using namespace std;3void move(char p1,char p2){4 cout<<p1<<"->"<<p2<<endl;5 }67//将a上的n个盘⼦经b移动到c上8void hanoi(int n,char a,char b,char c){9if(n == 0) return;//当a上没有盘⼦的时候,直接返回不需要移动10if(n == 1) move(a,c);//当a上只有⼀个盘⼦的时候,直接将盘⼦从a上移动到c上11if(n>1){12 hanoi(n-1,a,c,b);13 move(a,b);14 hanoi(n-1,c,b,a);15 }16 }1718int main(){19char x,y,z;20 x = 'a';21 y = 'b';22 z = 'c';23 hanoi(4,x,y,z);24return0;25 }Hanoi塔2.2分治法的基本思想分治法的基本思想:将⼀个规模为n的问题分解为k个规模较⼩的⼦问题,这些⼦问题相互独⽴且与原问题相同在使⽤分治法设计算法的时候,最好使⼦问题的规模⼤致相同,即将⼀个问题分为⼤⼩相等的k个⼦问题,⼀般情况k取2.※※※分治法中⽐较著名的是划分整数问题1、整数划分问题将⼀个正整数n表⽰为⼀系列正整数之和,n = n1 + n2 +…+nk其中n1≥n2≥…≥nk≥1, k≥1。
第2章递归与分治策略.
2.2 分治法的基本思想
分治法的基本思想
分治法的基本思想是将一个规模为n的问题分
解为k个规模较小的子问题,这些子问题互相 独立且与原问题相同。 对这k个子问题分别求解。如果子问题的规模 仍然不够小,则再划分为k个子问题,如此递 归的进行下去,直到问题规模足够小,很容易 求出其解为止。 将求出的小规模的问题的解合并为一个更大规 模的问题的解,自底向上逐步求出原来问题的 解。
二分搜索实例:设在A[8]中顺序放了以下9个元素:
A[0] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8]
-15 -6
③
0
③
7
④
9
①
23
54
②
82 101
③ ④
②
搜索x=9 9= =A[4], 一次比较,成功, 最好情况 搜索x=-15, -15<A[4], -15<A[1], -15= =A[0], 3次比较, 成功 搜索x=101 101>A[4], 101>A[6], 101>A[7], 101= =A[8], 4次比较, 成功 搜索x=8 8<A[4], 8>A[1], 8>A[2], 8>A[3], 4次比较, 不成功检索
1 5 1 F (n) 2 5
1 5 2
n 1
递归小结
优点:结构清晰,可读性强,而且容易用数学 归纳法来证明算法的正确性,因此它为设计算 法、调试程序带来很大方便。
缺点:递归算法的运行效率较低,无论是耗费 的计算时间还是占用的存储空间都比非递归算 法要多。
边界条件
1 n0 F ( n) 1 n 1 F (n 1) F (n 2) n 1
《计算机算法设计与分析》第二章 递归与分治策略PPT课件
{
if (n <= 1) return 1;
return fibonacci(n-1)+fibonacci(n-2);
} 2020/7/31
10
例3 Ackerman函数 当一个函数及它的一个变量是由函数自身定义时,称这
个函数是双递归函数。 Ackerman函数A(n,m)定义如下:
A(1,0) 2
12
例3 Ackerman函数
A(n,m)的自变量m的每一个值都定义了一个单变量函 数:
m=0时,A(n,0)=n+2
m=1时,A(n,1)=A(A(n-1,1),0)=A(n-1,1)+2,和 A(1,1)=2故A(n,1)=2*n
m=2时,A(n,2)=A(A(n-1,2),1)=2A(n-1,2),和 A(1,2)=A(A(0,2),1)=A(1,1)=2,故A(n,2)= 2n 。
第2章 递归与分治策略
2020/7/31
1
第一部分
整体概述
THE FIRST PART OF THE OVERALL OVERVIEW, PLEASE SUMMARIZE THE CONTENT
2
学习要点:
理解递归的概念。 掌握设计有效算法的分治策略。 通过下面的范例学习分治策略设计技巧。
当n=1时,perm(R)=(r),其中r是集合R中唯一的元素; 当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),…, (rn)perm(Rn)构成。
T(nn/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
new_第2章_递归与分治策略
递归举例
• 在以下三种情况下,常常要用到递归的方法。 • 1. 定义是递归的:有许多数学公式、数列等的定义是递归的。例如, 求n!和Fibonacci数列等。这些问题的求解过程可以将其递归定义直 接转化为对应的递归算法。
递归举例
• 2. 数据结构是递归的:有些数据结构是递归的。例如, 单链表就是一 种递归数据结构,其结点类型定义如下,该定义中,结构体LNode的定 义中用到了它自身,即指针域next是一种指向自身类型的指针,所以它 是一种递归数据结构。
分治法的概念
• 举例:有一个老板有一袋金块。每个月将有两名雇员会因其优异的表 现分别被奖励一个金块。按规矩,排名第一的雇员将得到袋中最重的 金块,排名第二的雇员将得到袋中最轻的金块。根据这种方式,除非 有新的金块加入袋中,否则第一名雇员所得到的金块总是比第二名雇 员所得到的金块重。因为有新的金块周期性的加入袋中,所以每个月 都必须找出最轻和最重的金块。假设有一台比较重量的仪器,我们希 望用最少的比较次数找出最轻和最重的金块。 • 解:如果采用先比较其中两个金块,再拿其中重的和袋中剩下的依次 比较,则需要n-1次轻的金块,则共需比较2n-3次。本问题可转化为求最 大数、最小数问题。
分治法的概念
• 举例:有一个老板有一袋金块。每个月将有两名雇员会因其优异的表 现分别被奖励一个金块。按规矩,排名第一的雇员将得到袋中最重的 金块,排名第二的雇员将得到袋中最轻的金块。根据这种方式,除非 有新的金块加入袋中,否则第一名雇员所得到的金块总是比第二名雇 员所得到的金块重。因为有新的金块周期性的加入袋中,所以每个月 都必须找出最轻和最重的金块。假设有一台比较重量的仪器,我们希 望用最少的比较次数找出最轻和最重的金块。
递归与堆栈
• 程序的主要执行过程: • (5)CPU回到 cs:000EH处(即call指令后面的指令处)继续 执行。
第2章递归与分治策略PPT课件
算法总体思想
将要求解的较大规模的问题分割成k个更小规模的子问题。 对这k个子问题分别求解。如果子问题的规模仍然不够小,则
再划分为k个子问题,如此递归的进行下去,直到问题规模足 够小,很容易求出其解为止。
4
算法总体思想
将求出的小规模的问题的解合并为一个更大规模的问 题的解,自底向上逐步求出原来问题的解。
A(1,0)2
A(0,m)1
m0
A(n,0)n2
n2
A(n,m)A(A(n1,m),m1) n,m1
10
2.1 递归的概念
例3 Ackerman函数 A(n,m)的自变量m的每一个值都定义了一个单变
量函数: M=0时,A(n,0) =n+2 M=1时,A(n,1) =A(A(n-1,1),0)=A(n-1,1)+2,
和A(1,1)=2故A(n,1)=2*n M=2时,A(n,2) =A(A(n-1,2),1)=2A(n-1,2),和
A(1,2)=A(A(0,2),1)=A(1,1)=2,故A(n,2)= 2^n 。
22 2 2
M=3时,类似的可以推出 n
M=4时,A(n,4)的增长速度非常快,以至于没有适源自当的数学式子来表示这一函数。
11
2.1 递归的概念
例4 排列问题 设计一个递归算法生成n个元素{r1,r2,…,rn}的全排列。 设R={r1,r2,…,rn}是要进行排列的n个元素,Ri=R-{ri}。 集合X中元素的全排列记为perm(X)。 (ri)perm(X)表示在全排列perm(X)的每一个排列前加上前 缀得到的排列。R的全排列可归纳定义如下:
(1) q(n,1)=1,n1; 当即最n大1 加 1 数n n 11不大于1时,任何正整数n只有一种划分形式,
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
15
2.6 棋盘覆盖
在一个2k×2k个方格组成的棋盘中,恰有一个方格与其他方 格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。 在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆 盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2 个L型骨牌不得重叠覆盖。 易知,覆盖任意一个2k×2k的特殊棋盘,用到的骨牌数恰好 为(4K-1)/3。
17
算法描述
void CB(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);} // 覆盖左下角子棋盘 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);}
人们从大量实践中发现,在用分治法设计算法时,最好使子 问题的规模大致相同。即将一个问题分成大小相等的k个子 问题的处理方法是行之有效的。这种使子问题规模大致相等 的做法是出自一种平衡(balancing)子问题的思想,它几乎总 是比子问题规模不等的做法要好。
5
2.2 分治法的基本思想
分治法的复杂性分析
7
算法及其复杂性
据此容易设计出二分搜索算法: public static int binarySearch(int [] a, int x, int n) { // 在 a[0] <= a[1] <= ... <= a[n-1] 中搜索 x // 找到x时返回其在数组中的位置,否则返回-1 int left = 0; int right = n - 1; 1 n 1 n T n T while (left <= right) { 1 n 1 2 int middle = (left + right)/2; if (x == a[middle]) return middle; T 1 1 if (x > a[middle]) left = middle + 1; T 2 T 1 1 2 T 4 T 2 1 3 else right = middle - 1; } T n T 2 m T 2 m 1 1 return -1; // 未找到x m 1 log n 1 O log n } 算法复杂度分析:每执行一次算法的while循环, 待搜索数组的大小减少一半。 因此,在最坏情况下,while循环被执行了O(logn) 次。循环体内运算需要O(1) 时间,因此整个算法在最坏情况下的计算时间复杂性为O(logn) 。
12
简单分治法求矩阵乘
首先假定n是2的幂。使用与上例类似的技术,将矩阵A,B和C中每一矩 阵都分块成4个大小相等的子矩阵。由此可将方程C=AB重写为:
C11 C12 A11 C A 21 C22 21
由此可得:
A12 B11 A22 B21
8
2.4 大整数的乘法
设计一个有效的算法,可以进行两个n位大整数的 乘法运算 小学的方法:O(n2) 效率太低 分治法:
X=a2n/2+b Y=c2n/2+d XY=ac2n+(ad+bc)2n/2+bd
n/2位 n/2位 n/2位 n/2位
X=
A
B
Y=
C
D
复杂度分析
• •
这条特征涉及到分治法的效率,如果各子问题是不独立 的,则分治法要做许多不必要的工作,重复地解公共的 子问题,此时虽然也可用分治法,但一般用动态规划较 好。
4
2.2 分治法的基本思想
分治法的基本步骤
divide-and-conquer(P)
{ if ( | P | <= n0) 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); //将各子问题的解合并为原问题的解 }
O1 T n 7T n / 2 O n 2
n2 n2
14
T(n)=O(nlog7) =O(n2.81) 较大的改进
更快的方法
Hopcroft和Kerr已经证明(1971),计算2个2×2矩 阵的乘积,7次乘法是必要的。因此,要想进一步 改进矩阵乘法的时间复杂性,就不能再基于计算 2×2矩阵的7次乘法这样的方法了。或许应当研究 3×3或5×5矩阵的更好算法。 在Strassen之后又有许多算法改进了矩阵乘法的计 算时间复杂性。 目前最好的计算时间上界是 O(n2.376) 是否能找到O(n2)的算法?目前为止还没有结果。
凡治众如治寡,分数是也。
——孙子兵法
3
2.2 分治法的基本思想
分治法的适用条件
分治法所能解决的问题一般具有以下几个特征:
• • 该问题的规模缩小到一定的程度就可以容易地解决; 该问题可以分解为若干个规模较小的相同问题,即该问题具有 最优子结构性质 利用该问题分解出的子问题的解可以合并为该问题的解; 该问题所分解出的各个子问题是相互独立的,即子问题之间不 包含公共的子问题。
复杂性:
这两个算式看起来更复杂一些,但它们仅需要3次n/2位 乘法[ac、bd和(a±c)(b±d)],于是
O1 n 1 T n 3T n / 2 On n 1
T(n)=O(nlog3) =O(n1.59) 较大的改进
细节问题:两个XY的复杂度都是O(nlog3),但考虑到a+c,bA12 B21
复杂度分析
C12 A11 B12 A12 B22
C21 A21 B11 A22 B21 C22 A21 B12 A22 B22
O1 T n 8T n / 2 O n 2
11
2.5 Strassen矩阵乘法
n×n矩阵A和B的乘积矩阵C中的元素C[i,j]定 义为:
n
cij aik bkj
k 1
若依此定义来计算A和B的乘积矩阵C,则每 计算C的一个元素C[i][j],需要做n次乘法和 n-1次加法。因此,算出矩阵C的 个元素所需 的计算时间为O(n3)
算法设计与分析
第3章 分治策略
2
3.1 分治法的基本思想
分治法的基本思想
分治法的基本思想是将一个规模为n的问题分解为k个规 模较小的子问题,这些子问题互相独立且与原问题相同。 对这k个子问题分别求解。如果子问题的规模仍然不够小, 则再划分为k个子问题,如此递归的进行下去,直到问题 规模足够小,很容易求出其解为止。 将求出的小规模的问题的解合并为一个更大规模的问题 的解,自底向上逐步求出原来问题的解。 分治法的设计思想是,将一个难以直接解决的大问题, 分割成一些规模较小的相同问题,以便各个击破,分而 治之。
O(1) n 1 T (n) kT (n / m) f (n) n 1
logm k
T (n) n
logm n 1
k
j 0
j
f (n / m )
j
6
2.3 二分搜索技术
给定已按升序排好序的n个元素a[0:n-1],现要在这n 个元素中找出一特定元素x。 适用分治法求解问题的基本特征:
一个分治法将规模为n的问题分成 k个规模为n/m的子问题去解。 设分解阀值n0=1,且adhoc解规模 为1的问题耗费1个单位时间。再 设将原问题分解为k个子问题以及 用merge将k个子问题的解合并为 原问题的解需用f(n)个单位时间。 用T(n)表示该分治法解规模为 |P|=n的问题所需的计算时间,则 有(右上)。 通过迭代法求得方程解(右下) 。 注意:递归方程及其解只给出n等 于m的方幂时T(n)的值,但是如果 认为T(n)足够平滑,那么由n等于 m的方幂时T(n)的值可以估计T(n) 的增长速度。通常假定T(n)是单 调上升的,从而当mi≤n<mi+1时, T(mi)≤T(n)<T(mi+1)。