Introduction_to_Algorithms_chap4
来源:互联网 发布:网络系统工程师简历 编辑:程序博客网 时间:2024/05/17 23:59
第四章 分治策略
最大子数组问题(分治法)
问题描述:
给出数组A,寻找A的最大的非空连续子数组,其中A不能全为非负数。
问题分析:
1、最简单的方法便是暴力搜索,找到最大的子数组,所需要的时间为Θ(n^2)。
2、使用分治的策略,分治法就是把一个大的问题简化为几个小的问题来进行求解。我们可以把数组A从中点分割成两个子数组L,R;那么最大子数组问题简化为以下三种情况:
- 最大子数组位于L中,
- 最大子数组位于R中,
- 最大子数组跨越中点,一部分在L中,一部分在R中。
那么我们就可以使用递归的方式来进行求解了
伪代码:
FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high):1 left_sum = INT_MIN2 sum = 03 for i = mid downto low4 sum = sum + A[i]5 if sum > left_sum6 left_sum = sum7 left_max = i8 right_sum = INT_MIN9 sum = 010 for i = mid+1 to high11 sum = sum + A[i]12 if sum > right_sum13 right_sum = sum14 right_max = i15 return (left_max, right_max, left_sum + right_sum)//FIND_MAX_SUBARRAY(A, low, high):1 if low == high2 return (low, high, A[low])3 else4 mid = (low+high)/2 //向下取整5 (left_low, left_high, left_sum) = FIND_MAX_SUBARRAY(A, low, mid)6 (right_low, right_high, right_sum) = FIND_MAX_SUBARRAY(A, mid+1, high)7 (cross_low, cross_high, cross_sum) = FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high)8 if left_sum >= right_sum and left_sum >= cross_sum9 return (left_low, left_high, left_sum)10 else if right_sum >= left_sum and right_sum >= cross_sum11 return (right_low, right_high, right_sum)12 else13 return (cross_low, cross_high, cross_sum)
c语言实现代码详见 github
练习
4.1-1 当A的所有元素均为负数时,FIND-MAX-SUBARRAY返回什么?
返回第一个元素的值。
4.1-2 对最大子数组问题,编写暴力求解方法的伪代码,其运行时间应该为Θ(n^2)。
FIND_MAX_SUBARRAY(A, low, high):1 max = INT_MIN2 start = end = low3 for i = low to high4 sum = A[i]5 for j = i+1 to high6 sum = sum + A[j]7 if (sum >= max)8 max = sum9 start = i10 end = j11 return (start, end, max)
4.1-4 假定修改最大子数组问题的定义,允许结果为空子数组,其和为0。你应该如何修改现有算法,使他们能允许空子数组为最终结果?
在计算完之后,看一看最终结果是否小于0,如果小于0则最终结果返回空子数组。
4.1-5 使用如下思想为最大子数组问题设计一个非递归的、线性时间的算法。从数组的做边界开始,由左至右处理,记录到目前为止已经处理过的最大子数组。若已知A[1..j]的最大子数组,基于如下性质将解扩展为A[1..j+1]的最大子数组:A[1..j+1]的最大子数组要么是A[1..j]的最大子数组,要么是某个子数组A[i..j+1] (1 <= i <= j+1)。在已知A[1..j]的最大子数组的情况下,可以在线性时间内找出形如A[1..j+1]的最大子数组。
伪代码:
FIND_MAX_SUBARRAY_BY_LINEAR_METHOD(A, low, high):1 (start, end, sum) = (low, low, A[low])2 tmp = A[low]3 for i = low+1 to high4 if tmp > 05 tmp = tmp + A[i]6 else7 tmp = A[i]8 start = i;9 if tmp > sum10 sum = tmp11 end = i;12 return (start, end, sum)
c语言实现代码详见 github
4.2-2 为Strassen算法编写伪代码(伪代码之后会附上C语言实现的代码)
伪代码:
SQUARE_MATRIX_MULTIPLY_BY_STRASSEN(A, B, n):1 let C be a new n × n matrix2 if n == 13 C[1][1] = A[1][1] * B[1][1]4 else5 partition A, B and C as in equations //具体如何分解请看C语言代码6 S1 = B12 - B227 S2 = A11 + A128 S3 = A21 + A229 S4 = B21 - B1110 S5 = A11 + A2211 S6 = B11 + B2212 S7 = A12 - A2213 S8 = B21 + B2214 S9 = A11 - A2115 S10 = B11 + B1216 P1 = SQUARE_MATRIX_MULTIPLY_BY_STRASSEN(A11, S1, n/2)17 P2 = SQUARE_MATRIX_MULTIPLY_BY_STRASSEN(S2, B22, n/2)18 P3 = SQUARE_MATRIX_MULTIPLY_BY_STRASSEN(S3, B11, n/2)19 P4 = SQUARE_MATRIX_MULTIPLY_BY_STRASSEN(A22, S4, n/2)20 P5 = SQUARE_MATRIX_MULTIPLY_BY_STRASSEN(S5, S6, n/2)21 P6 = SQUARE_MATRIX_MULTIPLY_BY_STRASSEN(S7, S8, n/2)22 P7 = SQUARE_MATRIX_MULTIPLY_BY_STRASSEN(S9, S10, n/2)23 C11 = P5 + P4 - P2 + P624 C12 = P1 + P225 C21 = P3 + P426 C22 = P5 + P1 - P3 - P727 return C
c语言实现代码详见 github
4.2-3 如何修改Strassen算法,使之适应矩阵规模n不是2的幂的情况?证明:算法的运行时间为Θ(n^log7)。
- 当n的值不是2^k的时候,寻找最近的k值,2^k < n < 2^(k+1),将A,B矩阵的行和列分为4部分,两部分为方阵,可以使用Strassen算法求解,剩下的非方阵部分用朴素法求解。
- 也可以在行列上补0,构造成一个2^k的方阵。
需要注意的是,在实际运用中,往往使用Strassen算法耗时不但没有减少,反而会增多,其原因主要是:
- 采用Strassen算法作递归运算,需要创建大量的动态二维数组,其中分配堆内存空间将占用大量的计算时间,从而掩盖了Strassen算法的优势;
- 对Strassen算法做出改进,设定一个界限,当n < 界限的时候,使用朴素法计算矩阵;
- 矩阵乘法一般意义上还是选择朴素法,只有当矩阵变稠密,而且矩阵的阶数很大时,才会考虑使用Strassen算法。
具体分析可以点击这里
4.2-4 如果可以用k次乘法操作(假定乘法的交换律不成立)完成两个3×3矩阵相乘,那么你可以在Θ(n^log7)时间内完成n×n矩阵相乘,满足这一条件的最大的k是多少?此算法的运行时间是怎么样的?
可以使用分治的思想,递归计算结果:
T(n) = kT(n/3) + Θ(n^2)
根据计算时间复杂度的主方法:
- 如果n^(log3 k) = n^2, 那么k = 9,算法运行时间为Θ(n^2 × lgn)。
- 如果k < 9,那么算法运行时间为Θ(n^2)。
- 如果 9 < k < 3^lg7(约等于 21.85)算法的运行时间为Θ(n^2.80)。
所以k最大整数值可以取21。
4.2-5 V.Pan发现一种方法,可以用132464次乘法操作完成68×68的矩阵相乘,发现另一种方法,可以用143640次乘法操作完成70×70的矩阵相乘,还发现一种方法,可以用155424次乘法操作完成72×72的矩阵相乘。当用于矩阵相乘的分治算法时,上述那种方法会得到最佳的渐近运行时间?与Strassen算法相比,性能如何?
根据计算,第一种方法将会的到最好的渐近时间。相比于Strassen算法,这种方法的性能更好一些。
4.2-6 用Strassen算法作为子进程来进行一个kn×n矩阵和一个n×kn矩阵相乘,最快需要花费多长时间?对两个输入矩阵规模互换的情况,回答相同的问题。
对于kn×n的情况来看,最后得到一个kn×kn的矩阵,可以看成是一个k×1的矩阵和一个1×k的矩阵相乘,每一次运算花费的时间都是Θ(n^2.81),总共进行k次Strassen算法的矩阵相乘,所以话费的时间为Θ(k*n^2.81).同理,规模互换之后的情况所花费的时间也是Θ(k*n^2.81)。
4.2-7 设计算法,仅使用三次实数乘法即可完成复数a+bi和c+di相乘。算法需要接收a、b、c和d为输入,分别生成实部ac-bd和虚部ad+bc.
4.3-1 证明:T(n) = T(n-1) + n的解为O(n^2)。
T(n) <= (n-1)^2 + n = n^2 - n + 1 <= n^2
0 0
- Introduction_to_Algorithms_chap4
- 数码管循环右移
- 计算几何相关知识清单
- C
- 系统设计的三要素
- [矩阵 点分治] BZOJ 4623 Styx
- Introduction_to_Algorithms_chap4
- 2017年3月17日,周结(四),学生信息管理系统
- 阻止冒泡的兼容性写法,以及阻止浏览器默认事件
- HDU2795线段树
- BZOJ 1188 [SG定理][博弈论]
- Introduction_to_Algorithms_chap5
- 洛谷 P1017 进制转换
- javaweb学习总结(七)——HttpServletResponse对象(一)
- 如何获取一个APK的包名