算法引论——数学归纳法

原来我已经有两个月没有更新博客了。这段时间写了篇论文,做了个app,其间还生了一场病。现在论文还没发表,app的界面也还在等着老弟继续支援,不过应该下个月就能发布了。原本很有冲劲的,突然间感觉累了,所以还是看书了。

 

忘了是哪个牛人推荐的《算法引论》(Introduction to Algorithm, A creative approach)了,两个月前借的书,续借了一次,到现在才终于有幸翻开来。果然是一本好书,连第二章的《数学归纳法》都没让我却步。希望我能够坚持着把这本书看完,然后坚持每一章都做笔记。书本后面还有很多练习题,我也想尽量做一点。嗯,就是这样了。这一次总结的是第一章和第二章。

 

第一章是对算法的概念进行简要的描述,其中有一段话我觉得说得既亲切又贴切:“计算机能够处理数十亿、数万亿比特单位的信息,能在一秒内完成数百万条基本指令。在这个数量级上进行算法设计是一种崭新的实践,有很多方面会与我们的直觉相反,因为我们通常只对自己能感知的事物进行思考。”在后面的习题中,有一道题是拿了一堆数字让我们排序的,面对这一堆数字,如果不让我借助电脑,我只能选择了冒泡排序,而快排,感觉就像与我们的直觉相反,因为大脑并不能像计算机那样记住很多数字。然后是做了练习题里的1.3,题目如下:
考虑由下列数所组成的表。你的工作是删去尽可能少的数使得留下来的数以升序排列。例如下面,你可以把9、44后面的数字全删掉,得到2个升序的数,也可以把9、44以及后面的83留下来得到3个升序的数,那么第二种方案就比第一种方案要好。这是数据:
9 44 32 12 7 42 34 92 35 37 41 8 20 27 83 64 61 28 39 93 29 17 13 14 55
21 66 72 23 73 99 1 2 88 77 3 65 83 84 62 5 11 74 68 76 78 67 75 69 70 22
71 24 25 26
我手工做了一遍,后来也因此推导出了算法的过程,做了下面的实现:

#include<stdio.h>
//an integer array of num, return the longest array that is in order by remove some of the numbers.
int orderredNumber( int* numbers, int num, int*& result ){
	int* order = new int[num];
	int* levelMin = new int[num];
	int level=0;
	levelMin[level]=numbers[0];
	order[0]= level;
	for( int i=1; i<num; i++ ){
		int j=0;
		for( j=0; j<=level; j++ )
			if( numbers[i] < levelMin[j] ){
				order[i] = j;
				levelMin[j]=numbers[i];
				break;
			}
		if( j == level+1 ){
			levelMin[j]=numbers[i];
			order[i]=j;
			level++;
		}
	}
	
	result = new int[level+1];
	int level2 = level;
	int i;
	for( i=num-1; i>=0; i-- ){
		if( (order[i] == level2) ){
			result[level2] = numbers[i];
			level2--;
			break;
		}
	}
	
	for( i; i>=0; i-- ){
		if( level2 < 0 ){
			break;
		}
		if( (order[i] == level2) && (numbers[i]<result[level2-1]) ){
			result[level2] = numbers[i];
			level2--;
		}
	}
	
	delete levelMin;
	delete order;
	return level;
			
}

int main(){
	int data[55] = {9, 44, 32, 12, 7, 42, 34, 92, 35, 37, 41, 8, 20, 27, 83, 64, 61, 28, 39, 93, 
		29, 17, 13, 14, 55, 21, 66, 72, 23, 73, 99, 1, 2, 88, 77, 3, 65, 83, 84, 62, 5, 11, 74, 68, 
	76, 78, 67, 75, 69, 70, 22, 71, 24, 25, 26};
	int* result;
	int level;
	level = orderredNumber( data, 55, result );
	printf( "largerst number %d\n", level );
	//printf( "%d", result[level-1] );
	for( int i=0; i<=level; i++ )
		printf( "%d ", result[i] );
	printf( "\n" );
	delete result;
}	 

  

这是我靠”直觉“做出来的,也许存在更好的方法。如果没有分析错误的话,这个算法的复杂度是O(n)。

 

第二章用数学归纳法证明了一些命题。首先给出我们熟悉的数学归纳法的基本形式:
设T为欲证的定理,若T中含有变量n,n取一切自然数(即正整数),那么不必直接证明对于所有n,T都成立,只需要证明下面的两个条件成立:
1. 当n=1时,T成立;
2. 对任意n>1,如果n-1时T成立,那么n时T也成立。
由基本的数学归纳法产生了多种变形:
1. 强归纳法:如果对于带有参数n的命题P,当n=1时P成立;并且如果对每一个n(n>1),若对任意小于n的自然数P成立时能推出n对命题P也成立,那么对任意自然数,P都成立。
2. 如果对于带有参数n的命题P,当n=1和n=2时P都成立,并且如果对每一个n(n>2),若n-2时P成立能推出n时P也成立,那么对任意自然数,P都成立。
3. 如果对于带有参数n的命题P,当n=1时P成立;并且如果对每一个n(n大于1且为2的整数幂),若n/2时命题P成立能推出对n命题P也成立;那么对任意一个2的整数幂的自然数,P都成立。

 

下面是一些命题的证明。


1. 若平面上的直线,任意二线不平行且任意三线不共点,则称这些直线居一般位置。下面计算n条居一般位置的直线能在平面上构成多少个区域。当n=1时,直线把平面分成两个区域;再加上一条边,分成4个区域;当n=3时,总共有7个区域。可以看到,似乎添加第i条直线会增加i个区域。如果是这样的话,那么n条居一般位置的直线能在平面上构成多少个区域就可以用1+1+2+...+i+...n来计算了。所以首先要证明,添加第i条直线会增加i个区域。
(1)当n<=3时,命题成立。
(2)归纳假设:在n-1条居一般位置的直线的平面上添加一条直线,会增加n个区域。现在证明,在n条居一般位置的直线的平面上添加一条直线,会增加n+1个区域。重点在于,由于直线居一般位置,因此一条直线要么穿过某个域将其一分为二,要么与该域不相交;而且第n条直线跟第n+1条直线必然相交于某个域。假如未曾加上第n条直线,添加第n+1条直线会增加n个区域(我们的归纳假设);把第n条直接重新放回去,与第n+1条直线相交于某区域R,原先第n+1条直线经过R时将R分割成两个域,所以只增加了一个域;现在R在n+1条直接未添加时就已经被分割成了两个域,也就说,第n+1条直线将被分割成两个域的R又分割成了4个域,因此增加了两个域,所以第n+1条直线增加了(n+1)个域。

 

2. 证明: 1/2 + 1/4 + 1/8 + ... + 1/2^n < 1
(1)当n=1时,1/2<1,命题成立。
(2)假设命题对n成立,即有1/2 + 1/4 + 1/8 + ... + 1/2^n < 1,现在必须证明,1/2 + 1/4 + 1/8 + ... + 1/2^n + 1/2^(n+1) < 1成立。
这里用到了比较巧妙的方法,首先由归纳假设有,1/2(1/2 + 1/4 + 1/8 + ... + 1/2^n) = 1/4 + 1/8 + .. + 1/2^(n+1) < 1/2,两边再同时加上1/2,即有:
1/2 + 1/4 + 1/8 + ... + 1/2^n + 1/2^(n+1) < 1

 

3. 证明:令G=(V,E)是一个有向图。G中存在一条独立集S(G),使得G中的每一个节点都可以从S(G)的某一个节点通过一条长度不超过2的路达到。
(1)设n为G的结点数,当n<=3时命题成立。
(2)假设对任意结点数小于n的有向图(强归纳法),定理成立。现在证明,对于结点数为n的有向图,定理成立。设v为G中的某个结点,令N(v)={v}U{w|w属于V且(v,w)属于E},令H为V-N(v)导出的子图,H的结点数小于n,由归纳假设,S(H)满足定理。
(2.1)假如S(H)U{v}是独立的,可以令S(G)=S(H)U{v},此时N(v)中的每个结点都可以通过S(G)中的v经过一步到达;而不在N(v)中的结点可以通过原先的S(H)通过不到2步的路达到;
(2.2)假如S(H)U{v}不是独立的,那么肯定存在w属于S(H)与v相邻,此时N(v)中的结点可以由w通过v两步达到;而而不在N(v)中的结点可以通过原先的S(H)通过不到2步的路达到,此时S(G)=S(H);

 

4. 证明:令G=(V,E)是连通的无向图,O是V中度数为奇数的节点的集合,我们可以把O中的节点分成节点对,在每一对中都能找到连接这两个节点的无重边的路。
(1)设m为G的边数,m=1时定理成立。
(2)假设定理对于所有边数小于m的连通无向图成立。
考虑有m条边的连通无向图G,令O是度数为奇数的节点的集合,如果O为空集,定理成立。否则,在O中任取两点,由于图连通,必然存在连通这两点的路。将该路连同结点删除,剩余的图的边比原来的少。然而此时图已经是不连通的了,我们修改归纳假设:定理对于所有边数小于m的无向图成立。对于边数为m的无向图,它可能不是连通的,而是被分成几个部分,由于同一部分连通,所以其O一定有偶数个结点。我们从中取两个节点并把连接这两点的路删除,所得到的图边数一定小于m,因此得证。

5. 这是柯西对对数学平均数和几何平均数定理的证明,确实如书上所言,非常漂亮。公式比较复杂,我决定截个图:

 

本书是国际算法大师乌迪·曼博(Udi Manber)博士撰写的一本享有盛誉的著作。全书共分12章:第1章到第4章为介绍性内容,涉及数学归纳法算法分析、数据结构等内容;第5章提出了与归纳证明进行类比的算法设计思想;第6章到第9章分别给出了4个领域的算法,如序列和集合的算法、图算法、几何算法、代数和数值算法;第10章涉及归约,也是第11章的序幕,而后者涉及NP完全问题;第12章则介绍了并行算法;最后是部分习题的答案及参考文献。本书的特色有二,旨在提高读者的问题求解能力,使读者能够理解算法设计的过程和思想:一是强调算法设计的创造性过程,注重算法设计背后的创造性思想,而不拘泥于某个具体算法的详细讨论;二是将算法设计类比于定理归纳证明,揭示了算法设计的基本思想和本质。 本书的组织结构清晰且易于理解,强调了创造性,具有浓郁特色,时至今日仍有其巨大的价值,并且适合作为计算机及相关专业算法和高级算法课程的教材。 第1章 引论 第2章 数学归纳法 2.1 引言 2.2 三个简单的例子 2.3 平面内区域的计数 2.4 简单的着色问题 2.5 复杂一些的加法题 2.6 一个简单的不等式 2.7 欧拉公式 2.8 图论中的一个问题 2.9 格雷码 2.10 在图上寻找无重边的路 2.11 数学平均数和几何平均数定理 2.12 循环不变量:将十进制数转换为二进制数 2.13 常见的错误 2.14 小结 第3章 算法分析 3.1 引言 3.2 符号O 3.3 时间与空间复杂度 3.4 求和 3.5 递推关系 3.5.1 巧妙地猜测 3.5.2 分治关系 3.5.3 涉及全部历史的递推关系 3.6 一些有用的证明论据 3.7 小结 第4章 数据结构简介 4.1 引言 4.2 基本数据结构 4.2.1 元素 4.2.2 数组 4.2.3 记录 4.2.4 链表 4.3 树 4.3.1 树的表示 4.3.2 堆 4.3.3 二叉搜索树 4.3.4 AVL树 4.4 散列 4.5 合并?查找问题 4.6 图 4.7 小结 第5章 基于归纳的算法设计 5.1 引言 5.2 多项式求值 5.3 最大导出子图 5.4 寻找一对一映射 5.5 社会名流问题 5.6 分治算法:轮廓问题 5.7 在二叉树中计算平衡因子 5.8 寻找最大连续子序列 5.9 增强归纳假设 5.10 动态规划:背包问题 5.11 常见的错误 5.12 小结 第6章 序列和集合的算法 6.1 引言 6.2 二叉搜索的几种形式 6.2.1 纯二叉搜索 6.2.2 循环序列的二叉搜索 6.2.3 二叉搜索特殊下标 6.2.4 二叉搜索长度未知的序列 6.2.5 重叠子序列问题 6.2.6 解方程 6.3 内插搜索 6.4 排序 6.4.1 桶排序和基数排序 6.4.2 插入排序和选择排序 6.4.3 归并排序 6.4.4 快速排序 6.4.5 堆排序 6.4.6 排序问题的下界 6.5 顺序统计 6.5.1 最大数和最小数 6.5.2 查找第k小的数 6.6 数据压缩 6.7 串匹配 6.8 序列比较 6.9 概率算法 6.9.1 随机数 6.9.2 着色问题 6.9.3 将拉斯维加斯算法变换成确定性算法 6.10 查找众数 6.11 三个展现有趣证明方法的问题 6.11.1 最长递增序列 6.11.2 查找集合中两个最大的元素 6.11.3 计算多重集合的模 6.12 小结 第7章 图算法 7.1 引言 7.2 欧拉图 7.3 图的遍历 7.3.1 深度优先搜索 7.3.2 广度优先搜索 7.4 拓扑排序 7.5 单源最短路径 7.6 最小代价生成树 7.7 全部最短路径 7.8 传递闭包 7.9 图的分解 7.9.1 双连通分支 7.9.2 强连通分支 7.9.3 利用图分解的例子 7.10 匹配 7.10.1 非常稠密图中的完美匹配 7.10.2 偶图匹配 7.11 网络流量 7.12 哈密尔顿旅行 7.12.1 反向归纳 7.12.2 在非常稠密图中找哈密尔顿回路 7.13 小结 第8章 几何算法 8.1 引言 8.2 判定点是否在多边形内部 8.3 构造简单多边形 8.4 凸包 8.4.1 直接方法 8.4.2 礼品包裹算法 8.4.3 Graham扫描算法 8.5 最近点对 8.6 水平线段和竖直线段的交点 8.7 小结 第9章 代数和数值算法 9.1 引言 9.2 求幂运算 9.3 欧几里得算法 9.4 多项式乘法 9.5 矩阵乘法 9.5.1 Winograd算法 9.5.2 Strassen算法 9.5.3 布尔矩阵 9.6 快速傅里叶变换 9.7 小结 第10章 归约 10.1 引言 10.2 归约的例子 10.2.1 简单字符串匹配问题 10.2.2 特殊代表集 10.2.3 关于序列比较的归约 10.2.4 在无向图中寻找三角形 10.3 有关线性规划的归约 10.3.1 概述与定义 10.3.2 归约到线性规划的例子 10.4 下界的归约 10.4.1 寻找简单多边形算法复杂度的下界 10.4.2 关于矩阵的简单归约 10.5 常见的错误 10.6 小结 第11章 NP完全问题 11.1 引言 11.2 多项式时间归约 11.3 非确定性和Cook定理 11.4 NP完全性的证明例子 11.4.1 顶点覆盖问题 11.4.2 支配集问题 11.4.3 3SAT问题 11.4.4 团问题 11.4.5 3着色问题 11.4.6 一般经验 11.4.7 更多的NP完全问题 11.5 处理NP完全问题的技术 11.5.1 回溯法和分枝限界法 11.5.2 确保性能的近似算法 11.6 小结 第12章 并行算法 12.1 引言 12.2 并行计算模型 12.3 共享存储器算法 12.3.1 并行加 12.3.2 寻找最大数的算法 12.3.3 并行前缀问题 12.3.4 在链表中查寻秩 12.3.5 欧拉遍历技术 12.4 互连网络上的算法 12.4.1 阵列上的排序 12.4.2 排序网络 12.4.3 在树中查找第k个最小元素 12.4.4 网孔上的矩阵乘法 12.4.5 超立方体中的路由 12.5 脉动计算 12.5.1 矩阵与向量相乘 12.5.2 卷积问题 12.5.3 序列的比较 12.6 小结 部分习题答案 参考文献
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值