《算法导论》学习笔记(二)

第3章 函数的增长

  这部分的内容和高中数学的数列部分内容相近,用极限的思想去看待问题,发现两者有惊人相似的本质,这与算法本身处理的数据是离散的这点本质密切相关。算法的时间复杂度用指令或者操作总次数来表述,那么这就涉及到数列的求和问题,其中包括数列的上界和下界,以及上确界和下确界(对应算法分析中的“渐进紧确界”asymptotically bound)。大体的思想和数列证明题相似,O记号和Ω记号表示上界和下界,而θ则表征时间复杂度本身的量级,就像一个“两边夹”的结果。


第4章 分治策略

  分解、解决、合并是解决复杂问题的三个策略,这在之前的合并排序法中已经有所体现,这也是递归的本质。递归问题需要考虑的本质是1.T(n)和T(n-1)之间的关系;2.边界问题(n=1)。

  4.1 最大子数组问题

 给定一个任意数组, 求出数组中连续区间中元素的最大和。如果采用枚举的方法,时间复杂度很容易可以推测得θ(n^2)(两个for循环),采用分治策略可以降低时间复杂度。具体思路为:

1.将数组分为两部分,则最大子数组可能落在:①前部分数组;②后部分数组;③两个子数组的交界处。

2.可以预见,由于不断将数组分成两半,本质上会生成一个二叉树,而二叉树的层数为log2(n),因此我们只需关注搜寻第三种情况的时间复杂度,再乘上log2(n)即可。

3.很明显,第三种情况计算的时间复杂度为线性的,即θ(n),因为我们只需要从中点开始向两边拓展直到遇到负数,因此只需要一层循环。因此,通过分治策略可以将时间复杂度降为θ(log2(n)*n)。

代码如下:

#include 
#include 
using namespace std;
int p[3] ={0,0,0};
int p1[3] ={0,0,0};
int p2[3] ={0,0,0};
int p3[3] ={0,0,0};

void Find_Max_Cross_Sub(int *A,int low,int mid,int high,int *p){
	int left_sum = -INT_MAX;
	int sum = 0;
	int max_left;
	for(int i=mid;i>=low;i--){
		sum += A[i];
		if(sum > left_sum){
			left_sum = sum;
			max_left = i;
		}
	}
	int right_sum = -INT_MAX;
	int max_right;
	sum = 0;
	for(int i=mid+1;i<=high;i++){
		sum += A[i];
		if(sum > right_sum){
			right_sum = sum;
			max_right = i;
		}
	}
	p[0] = max_left;
	p[1] = max_right;
	p[2] = right_sum+left_sum;
}

void *Find_Maximun_Sub(int* A,int low,int high,int *p){
	if(high == low){
		p[0] = low;
		p[1] = high;
		p[2] = A[low];
	}
	else{
		int mid = (low+high)/2;
		Find_Maximun_Sub(A,low,mid,p1);
		Find_Maximun_Sub(A,mid+1,high,p2);
		Find_Max_Cross_Sub(A,low,mid,high,p3);
		if(p1[2]>=p2[2]&&p1[2]>=p3[2]){
			p[0] = p1[0];
			p[1] = p1[1];
			p[2] = p1[2];
		}
		else if(p2[2]>=p1[2]&&p2[2]>=p3[2]){
			p[0] = p2[0];
			p[1] = p2[1];
			p[2] = p2[2];
		}
		else{
			p[0] = p3[0];
			p[1] = p3[1];
			p[2] = p3[2];
		}
	}
	return 0;
}

int main(){
	int A[6] = {-11,2,3,5,4,-3};
	Find_Maximun_Sub(A,0,5,p);
	cout<

4.2 矩阵乘法的Strassen

矩阵乘法,对于n阶的一个矩阵就有n^2个元素,两个n阶矩阵相乘,必须用3层循环来计算,即θ(n^3),用分支的方法把两个相乘的矩阵分别分解成4个,各自计算,然而最终的时间复杂度仍然为θ(n^3),那么问题的本质在哪呢,这里的分治策略为何不起作用了?这个问题和之前的问题有什么本质的不同?这是教材没有说清楚的一点。个人认为,之前的问题之所以通过分治策略降低了时间复杂度,因为最初的算法做了许多无用功,而分治策略避免了无用功。此问题是,矩阵乘法哪怕是最简练的算法,n×n×n次乘法几乎是必须的(一般的分治策略无法避免这一点)。而实际上,矩阵的乘法,各元素间必定也存在计算重叠的部分,该算法的创造者用人为的脑力(公式的推导和证明)终于为矩阵乘法降低了复杂度,因为矩阵乘法的每一次计算并非完全独立,即一次计算的结果可能对下一次有帮助,作者经过严格证明,只用7次代替了之前的8次计算。把n^(log2(8))变成了n^(log2(7)),即θ(n^2.8)。数学证明比较巧妙,而代码实现则比较无聊。

  这个算法实现非常复杂,就不放出来了。


你可能感兴趣的:(算法)