4.1 最大子数组问题
4.1-1
当A的所有元素均为负数时,FIND-MAXIMUM-SUBARRAY返回一个最大的负数。
4.1-2
暴力求解最大子数组问题的伪代码:
VIOLENTLY-FIND-MAXIMUM-SUBARRAY(A, low, high)
if high == low
return (low, high, A[low])
else
max-sum = -∞
for i = low to high
sum = 0
for j = i to high
sum = sum + A[j]
if sum > max-sum
max-sum = sum
max-left = i
max-right = j
return (max-left, max-right, max-sum)
4.1-3
用C++实现最大子数组的暴力算法:
#include <iostream>
#include <random>
#include <ctime>
int violently_find_maximum_subarray(int[], int*, int*);
using namespace std;
int main() {
int test[20];
int low = 0, high = 19, max_sum;
uniform_int_distribution<int> u(-10, 10);
default_random_engine e(time(0));
for (size_t i = 0; i < 20; i++)
{
test[i] = u(e);
cout << test[i] << " ";
}
cout << endl;
max_sum = violently_find_maximum_subarray(test, &low, &high);
cout << "最大子数组和为:" << max_sum << endl;
for (size_t i = low; i < high + 1; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
int violently_find_maximum_subarray(int test[], int *low, int *high)
{
int left = *low, right = *high;
if (left == right)
{
return test[left];
}
else
{
int max_left, max_right, max_sum = test[left];
for (size_t i = left; i < right + 1; i++)
{
int sum = 0;
for (size_t j = i; j < right + 1; j++)
{
sum += test[j];
if (sum > max_sum)
{
max_sum = sum;
max_left = i;
max_right = j;
}
}
}
*low = max_left;
*high = max_right;
return max_sum;
}
}
用C++实现最大子数组问题的递归算法:
#include <iostream>
#include <random>
#include <ctime>
int recursively_find_maximum_subarray(int[], int*, int*);
using namespace std;
int main() {
int test[20];
int low = 0, high = 19, max_sum;
uniform_int_distribution<int> u(-10, 10);
default_random_engine e(time(0));
for (size_t i = 0; i < 20; i++)
{
test[i] = u(e);
cout << test[i] << " ";
}
cout << endl;
max_sum = recursively_find_maximum_subarray(test, &low, &high);
cout << "最大子数组和为:" << max_sum << endl;
for (size_t i = low; i < high + 1; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
int find_max_crossing_subarray(int sub[], int *low, int mid, int *high) {
int left = *low, right = *high;
int max_left = mid, left_sum = sub[mid];
int sum = 0;
for (int i = mid; i >= left; i--)
{
sum += sub[i];
if (sum > left_sum)
{
left_sum = sum;
max_left = i;
}
}
int max_right = mid + 1, right_sum = sub[mid + 1];
sum = 0;
for (int i = mid + 1; i <= right; i++)
{
sum += sub[i];
if (sum > right_sum)
{
right_sum = sum;
max_right = i;
}
}
*low = max_left;
*high = max_right;
return left_sum + right_sum;
}
int recursively_find_maximum_subarray(int test[], int *low, int *high)
{
int left = *low, right = *high;
if (left == right)
{
return test[left];
}
else
{
int mid = (left + right) / 2, left_sum, right_sum, cross_sum;
int left_low = left, left_high = mid;
int right_low = mid + 1, right_high = right;
int cross_low = left, cross_high = right;
left_sum = recursively_find_maximum_subarray(test, &left_low, &left_high);
right_sum = recursively_find_maximum_subarray(test, &right_low, &right_high);
cross_sum = find_max_crossing_subarray(test, &cross_low, mid, &cross_high);
if (left_sum >= right_sum && left_sum >= cross_sum)
{
*low = left_low;
*high = left_high;
return left_sum;
}
else if (right_sum >= left_sum && right_sum >= cross_sum)
{
*low = right_low;
*high = right_high;
return right_sum;
}
else
{
*low = cross_low;
*high = cross_high;
return cross_sum;
}
}
}
4.1-4
FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high)
left-sum = 0
max-left = 0
sum = 0
for i = mid downto low
sum = sum + A[i]
if sum > left-sum
left-sum = sum
max-left = i
right-sum = 0
max-right = 0
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 + right-sum)
FIND-MAXIMUM-SUBARRAY(A, low, high)
if high == low
if A[low] > 0
return (low, high, A[low])
else
return (0, 0, 0)
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)
4.1-5
最大子数组问题非递归的、线性时间的算法:
FIND-MAXIMUM-SUBARRAY(A, low, high)
max-left = low
while A[max-left] <= 0
max-left = max-left + 1
max-sum = A[max-left]
right-sum = 0
max-right-sum = 0
max-right-left = max-left
for i = low + 1 to high
if A[i] + max-right-sum > max-sum
max-sum = A[i] + max-right-sum
max-left = max-right-left + 1
max-right = i
right-sum = 0
max-right-sum = 0
max-right-left = i
elseif A[i] + right-sum > 0
max-sum = max-sum + A[i] + right-sum
max-right = i
right-sum = 0
max-right-sum = 0
max-right-left = i
else
right-sum = right-sum + A[i]
if max-right-sum <= 0
max-right-sum = A[i]
max-right-left = i - 1
else
max-right-sum = max-right-sum + A[i]
4.2 矩阵乘法的Strassen算法
4.2-1
步骤1:,
,
步骤2:
步骤3:
步骤4:
所以:
4.2-2
STRASSEN-MATRIX-MULTIPLY(A, B)
n = A.rows
let C be a new nXn matrix
if n == 1
c[1][1] = a[1][1] * b[1][1]
else partition A, B, and C as in equations(4.9)
S[1] = B[1][2] - B[2][2]
S[2] = A[1][1] + A[1][2]
S[3] = A[2][1] + A[2][2]
S[4] = B[2][1] - B[1][1]
S[5] = A[1][1] + A[2][2]
S[6] = B[1][1] + B[2][2]
S[7] = A[1][2] - A[2][2]
S[8] = B[2][1] + B[2][2]
S[9] = A[1][1] - A[2][1]
S[10] = B[1][1] + B[1][2]
P[1] = A[1][1] * S[1]
P[2] = S[2] * B[2][2]
P[3] = S[3] * B[1][1]
P[4] = A[2][2] * S[4]
P[5] = S[5] * S[6]
P[6] = S[7] * S[8]
P[7] = S[9] * S[10]
C[1][1] = P[5] + P[4] - P[2] + P[6]
C[1][2] = P[1] + P[2]
C[2][1] = P[3] + P[4]
C[2][2] = P[5] + P[1] - P[3] - P[7]
return C
4.2-3
添加一些全0的行列把矩阵的行列数补到2的幂,即可使Strassen算法适应矩阵规模n不是2的幂的情况。
证明:根据式(4.18)(描述Strassen算法运行时间T(n)的递归式),利用4.5节的主方法,对某个常数,有
,则
。
4.2-4
因为可以用k次乘法操作(假定乘法的交换律不成立)完成两个3X3矩阵相乘,所以。利用4.5节的主方法,比较
和
的大小:
- 如果
,则
。此时
且
。
- 如果
,则
。此时
且
。
- 如果
,则
。此时
且当
时
,即
,所以最大的整数k为21。
所以满足这一条件的最大的k是21。此算法的运行时间是(因为
)
4.2-5
根据练习4.2-4,应该选择下面三个数中最小的那个:,所以用143640次乘法操作完成70X70的矩阵相乘会得到最佳的渐进运行时间。
4.2-6
用Strassen算法作为子进程来进行一个矩阵和一个
矩阵相乘,最快需要花费
时间。对两个输入矩阵规模互换后,最快需要花费
时间。
4.2-7
plural_multiple(a, b, c, d)
A = (a + b)(c + d)
B = ac
C = bd
return B - C, A - B - C
4.3 用代入法求解递归式
4.3-1
证明:对某个恰当选出的常数,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立。
4.3-2
证明:对某个恰当选出的常数,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立。
4.3-3
证明:对某个恰当选出的常数,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立。
所以:递归式的解为
。
4.3-4
证明:对某个恰当选出的常数,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立。此时,边界条件
成立。
4.3-5
证明:
- 对某个恰当选出的常数
,假定
对所有正数
都成立,特别是对于
,有
,以及对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立。所以:递归式
。
- 对某个恰当选出的常数
,假定
对所有正数
都成立,特别是对于
,有
,以及对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立。所以:递归式
。
所以:归并排序的“严格”递归式(4.3)的解为。
4.3-6
证明:对某个恰当选出的常数,假定存在常数a使得
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立,所以:
的解为
。
4.3-7
- 对某个恰当选出的常数
,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。这并不意味着对任意c都有
。
- 对某个恰当选出的常数
,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立,得证。
4.3-8
- 对某个恰当选出的常数
,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。这并不意味着对任意c都有
。
- 对某个恰当选出的常数
,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立,得证。
4.3-9
令,得到
。现在重命名
,得到新的递归式:
。利用4.5节的主方法,得到
。再从
转换回
,得到
。
4.4 用递归树方法求解递归式
4.4-1
。
对某个恰当选出的常数,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。所以:递归式
。
4.4-2
。
对某个恰当选出的常数,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立,所以:递归式
。
4.4-3
。
对某个恰当选出的常数,假定
对所有正数
都成立。将其代入递归式,得到
。所以:递归式
。
4.4-4
。
对某个恰当选出的常数,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立,所以:递归式
。
4.4-5
对某个恰当选出的常数,假定
对所有正数
都成立,特别是对于
,有
,以及对于
,有
。将其代入递归式,得到
。其中,只要
,最后一步都会成立,所以:递归式
。
4.4-6
在递归树中具有最小深度的叶节点的深度为,前
层每层的代价为cn,所以递归式T(n)大于等于
,即
。
4.4-7
。
- 对某个恰当选出的常数
,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。所以:递归式
。
- 对某个恰当选出的常数
,假定
对所有正数
都成立,特别是对于
,有
。将其代入递归式,得到
。所以:递归式
。
4.4-8
。
4.4-9
。
4.5 用主方法求解递归式
4.5-1
a.a=2,b=4,,因此
。由于
,其中
,因此应用情况1,从而得到解
。
b.a=2,b=4,,因此
。由于
,因此应用情况2,从而得到解
。
c.a=2,b=4,,因此
。由于
,其中
,因此应用情况3,从而得到解
。
d.a=2,b=4,,因此
。由于
,其中
,因此应用情况3,从而得到解
。
4.5-2
- 当应用情况3时,应有a<16。此时
。
- 当应用情况2时,应有a=16。此时
。
- 当应用情况1时,同时为渐进快于Strassen算法,应有
即a<49。此时
。
综上,a的最大整数值应是48。
4.5-3
a=1,b=2,,因此
。由于
,因此应用情况2,从而得到解
。
4.5-4
a=4,b=2,,因此
。由于对任意正常数
,比值
都渐进小于
。因此,递归式落入了情况2和情况3之间的间隙,主方法不能应用于此递归式。
4.5-5
。
思考题
4-1
a.a=2,b=2,,因此
。由于
,其中
,因此应用情况3,从而得到解
。
b.a=1,b=10/7,,因此
。由于
,其中
,因此应用情况3,从而得到解
。
c.a=16,b=4,,因此
。由于
,因此应用情况2,从而得到解
。
d.a=7,b=3,,因此
。由于
,其中
,因此应用情况3,从而得到解
。
e.a=7,b=2,,因此
。由于
,其中
,因此应用情况1,从而得到解
。
f.a=2,b=4,,因此
。由于
,因此应用情况2,从而得到解
。
g.。
4-2
a.BINARY-SEARCH
(主方法情况2)。
。
(主方法情况3)。
b.MERGE-SORT
(主方法情况2)。
。
(主方法情况2)。
4-3
a.a=4,b=3,,因此
。由于
,其中
,因此应用情况1,从而得到解
。
b.。
c.a=4,b=2,,因此
。由于
,其中
,因此应用情况3,从而得到解
。
d.忽略-2项,则a=3,b=3,,因此
。由于
,因此应用情况2,从而得到解
。
e.。
f.,所以
。
,所以
。综上
。
g.。
h.。
i.。
j.令,得到
。现在重命名
,得到新的递归式:
。令
,得到新的递归式:
。利用4.5节的主方法,得到
,则
。再从
转换回
,得到
。
4-4
a.证明:。
b.证明:
- 由a得:
。
。
。
c.证明:因为当时,
。所以
。
d.证明:由c得:。因为当i>2时,
。
4-5
a.证明:令有g(g<n/2)块芯片是好的。在坏芯片中的某g块可以合谋起来,他们将彼此报告成好的,将其余的报告成坏的。因为好的g块芯片也会这么做,这两组芯片在逐对检测操作时的结果是对称的,所以使用任何基于这种逐对检测操作的策略,教授都不能确定哪些芯片是好的。
b.证明:先将芯片分成两组,然后每次从两组中各取一个进行检测。如果测试结果是第一种(两片都是好的,或都是坏的),则从这两块芯片中移除一块,此时移除的那块芯片可能是好的也可能是坏的。如果测试结果不是第一种,则将这两块芯片都移除,此时移除的两块芯片中至少一块是坏的。进行次逐对检测后,至多剩下
块芯片,其中超过一半的芯片是好的,此时已将问题规模减半。
c.证明:。a=1,b=2,
,因此
。由于
,其中
,因此应用情况3,从而得到解
。
4-6
a.证明:
- 对行使用归纳法:当k=i+1时,
。假设对于k=i+n,
成立。当k=k+1=i+n+1时,因为
和
,两边相加得
。
- 对列使用归纳法:当k=j+1时,
。假设对于k=j+n,
成立。当k=k+1=i+n+1时,因为
和
,两边相加得
。
b.
| 37 | 23 | 24 | 32 |
| 21 | 6 | 7 | 10 |
| 53 | 34 | 30 | 31 |
| 32 | 13 | 9 | 6 |
| 43 | 21 | 15 | 8 |
c.证明:假设且
。
,不符合Monge阵列的定义,所以不存在这样的i,j。
d.令表示第I行的最左最小元素的列下标,且当i<1时,
;当i>m时,
。因为
,计算
需要
。在偶数行的最左最小元素已知的情况下,计算奇数行的最左最小元素需要
。
e.证明:。
9633

被折叠的 条评论
为什么被折叠?



