第3章 分治策略
- 步骤:
- 分解
- 解决
- 合并
- 递归情况:当子问题足够大
- 基本情况:当子问题足够小
- 递归式:刻画分治算法的运行时间
- 求解递归式的方法:
- 代入法
- 递归树法
- 主方法
3.1 最大子数组
-
例题: 买股票,使利益最大化
-
思路:低价买进,高价卖出
-
暴力解决问题:尝试每对可能的买进和卖出日期组合。n天中共有 ( 2 n ) (_2^n) (2n)种日期组合。运行时间为 Ω ( n 2 ) \Omega(n^2) Ω(n2)
-
问题变换:寻找一段日期,使得从第一天到最后一天的股票净变值。问题转化为寻找A的和最大的非空连续子数组。称这样的连续子数组为最大子数组。
-
使用分治策略的求解方法:将子数组划分为两个规模尽量相等的子数组,分别求解两个子数组的最大子数组。
FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high)
left-sum = 负无穷 sum = 0 for i = mid downto low sum = sum + A[i] if sum > left-sum left-sum = sum max-left = i right-sum = 负无穷 sum = 0 for j = mid + 1 to high sum = sum + A[j] if sum > right-sum right-sum = sum max-right = j return(max-left, max-right, left-sum + reght-sum)
FIND-MAXIMUN-SUBARRAY(A, low, high)
if high == low return(low, high, A[low]) else mid=(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-CROSSING-SUBARRAY(A, low, mid, high) if left-sum>=right-sum and left-sum>=cross-sum return (left-low, left-high, left-sum) elseif right-sum>=left-sum and right-sum>=cross-sum return (right-low, right-high, right-sum) else return (cross-low, cross-high, cross-sum)
-
python代码
def FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high): left_sum = 0 sum = 0 for i in range(low, mid): sum = sum + A[i] if sum > left_sum: left_sum = sum right_sum = 0 sum = 0 for j in range(mid+1, high): sum = sum + A[j] if sum > right_sum: right_sum = sum return left_sum, right_sum, left_sum + right_sum def FIND_MAXIMUM_SUBARRAY(A, low, high): if high == low: return low, high, A[low] else: mid = (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_CROSSING_SUBARRAY(A, low, mid, high) if left_sum>=right_sum and left_sum>=cross_sum: return left_low, left_high, left_sum elif right_sum>=left_sum and right_sum>=cross_sum: return right_low, right_high, right_sum else: return cross_low, cross_high, cross_sum B = [2,4,5,-3,-5,2,-6,2,7,-5,4,1,3,6,-9,-2,-4,8] B_low = 0 B_high = 16 B_mid = 8 print(FIND_MAXIMUM_SUBARRAY(B, B_low, B_high))
-
分治算法的分析
- 运行时间:
{ θ , n = 1 2 T ( n / 2 ) + θ ( n ) , n > 1 \left\{ \begin{array}{c} \theta,n=1\\ 2T(n/2)+\theta(n),n>1 \end{array} \right. {θ,n=12T(n/2)+θ(n),n>1
- 运行时间:
3.2 矩阵乘法的Strassen算法
-
SQUARE-MATRIX-MULTIPLY(A,B)
n = A.rows let C be a new n×n matrix for i = 1 to n for j = 1 to n Cij = 0 for k = 1 to n Cij = Cij + aik * bkj return C
-
运行时间: θ ( n 3 ) \theta(n^3) θ(n3)
-
分治算法:
-
伪代码:SQUARE-MATRIX-MULTIPLY-RECURSIVE(A,B)
n = A.rows let C be a new n×n matrix if n == 1 c11=a11 * b11 else partition A, B, and C as in equations(4,9) C11=SQUARE-MATRIX-MULTIPLY-RECURSIVE(A11,B11) + SQUARE-MATRIX-MULTIPLY-RECURSIVE(A12,B21) C12=SQUARE-MATRIX-MULTIPLY-RECURSIVE(A11,B12) + SQUARE-MATRIX-MULTIPLY-RECURSIVE(A12,B22) C21=SQUARE-MATRIX-MULTIPLY-RECURSIVE(A21,B11) + SQUARE-MATRIX-MULTIPLY-RECURSIVE(A22,B21) C22=SQUARE-MATRIX-MULTIPLY-RECURSIVE(A21,B12) + SQUARE-MATRIX-MULTIPLY-RECURSIVE(A22,B22) return C
-
运行时间:
T ( n ) = { θ ( 1 ) , n = 1 8 T ( n / 2 ) + θ ( n 2 ) , n > 1 T(n)=\left\{ \begin{array}{c} \theta(1),n=1\\ 8T(n/2)+\theta(n^2),n>1 \end{array} \right. T(n)={θ(1),n=18T(n/2)+θ(n2),n>1
-
-
Strassen方法
-
步骤:
- 将输入矩阵A,B和输出矩阵C各分解为四个 n / 2 × n / 2 n/2\times n/2 n/2×n/2的子矩阵。
- 创建10个 n / 2 × n / 2 n/2\times n/2 n/2×n/2的空矩阵 S 1 , S 2 , ⋅ ⋅ ⋅ , S 10 S_1,S_2,···,S_{10} S1,S2,⋅⋅⋅,S10,每个矩阵保存步骤1中创建的两个子矩阵的和与差。花费时间为 θ ( n 2 ) \theta(n^2) θ(n2)。
- 用步骤1中创建的子矩阵和步骤2中创建的10个矩阵,递归地计算7个矩阵积 P 1 , P 2 , ⋅ ⋅ ⋅ , P 7 P_1,P_2,···,P_7 P1,P2,⋅⋅⋅,P7。每个矩阵 P i P_i Pi都是 n / 2 × n / 2 n/2\times n/2 n/2×n/2的。
- 通过 P i P_i Pi矩阵的不同组合进行加减运算,计算出结果矩阵C的子矩阵 C 11 , C 12 , C 21 , C 22 C_{11},C_{12},C_{21},C_{22} C11,C12,C21,C22。花费时间 θ ( n 2 ) \theta(n^2) θ(n2)。
-
运行时间:
T ( n ) = { θ ( 1 ) , n = 1 7 T ( n / 2 ) + θ ( n 2 ) , n > 1 T(n)=\left\{ \begin{array}{c} \theta(1),n=1\\ 7T(n/2)+\theta(n^2),n>1 \end{array} \right. T(n)={θ(1),n=17T(n/2)+θ(n2),n>1
-
3.3 用代入法求解递归式
- 代入法求解递归式:
- 步骤:
- 猜测解的形式
- 用数学归纳法求出解中的常数,并证明解是正确的
- 步骤:
- 做出好的猜测
- 避免陷阱
- 改变变量
3.4 用递归树方法求解递归式
- 递归树中,每个结点表示一个单一子问题的代价,子问题对应某次递归函数调用。创建递归树之后,对树的每层的各子问题的代价进行求和,得到每一层的代价,然后将所有层的代价加起来,得到整棵递归树的总代价,这个总代价就是递归式的解。
3.5 用主方法求解递归式
- 主定理:令 a≥1和b>1是常数, f ( n ) f(n) f(n)是一个函数, T ( n ) T(n) T(n)是定义在非负整数上的递归式: T ( n ) = a T ( n / b ) + f ( n ) T(n) = aT(n/b) + f(n) T(n)=aT(n/b)+f(n)