作为一本介绍算法技术和思想的书籍,本书不仅可以面向信息学科大学生作为基本的教材(或参考书),更是将任何具有初等数学基础的人引入算法应用与研究殿堂的一块引路石。本书循序渐进、深入浅出地展示了算法研究与应用中,从模型分析、算法构造到复杂性分析和算法优化的方方面面。涉及的内容从古老的算术算法、排序算法、简单图论到近现代出现的计算图论、贪心算法、分治算法、线性规划、动态规划、随机算法以及NP复杂性理论,甚至是尚未完全显现全貌的量子计算,覆盖了经典、现代和未来算法发展的众多代表性工作。
本书选材新颖,内容丰富,适用于作为计算机学科以及相关学科算法课程的教材和参考书,同时也可作为从事算法研究的一本好的入门书籍。
前 言
本书是在加州大学Berkeley分校和San Diego分校本科生算法课程讲义的基础上,历经十年,逐渐整理、日益完善而成的。我们教授此门课程的方法在过去几年间经历了巨大变革,它一方面照顾到了学生的背景(学生们除编程之外并不具备正式而完善的应用技巧),一方面反映了算法领域总体上走向成熟的趋势,正如过去数十年我们已经见证了的。随着当初的教学讲义被逐渐提炼成娓娓道来的文字,我们也逐渐调整着课程的结构,以突出教学材料编排中蕴含的“故事情节”。因此,本书的内容经过仔细选择后才得以结集成篇。我们不求把此书编成一本算法百科全书,这使我们可以自由地把大多数传统算法书籍未曾强调或忽略的主题包含进来。
我们根据学生的特点(这些特点也是当今计算机科学专业的大多数本科生所共有的),提炼出能使每个算法运转下去的简洁数学思想,而不是沉湎于正式而冗长的理论证明。换言之,我们在活力和刻板之间,更强调前者。我们发现,学生更能接受这种形式带来的数学的生命力。正是在这些简洁有力的数学思想的推动下,我们才得以展开我们的阐述。
一旦按照这种方式来理解算法,那么从它的历史本源开始研究就显得很有意义,并且,对于今天的我们来说,一方面,历史的主题看似那样的熟悉,另一方面,其与今天的对比却又是那样的显著:数论、素性测试和因子分解。这就是本书第一部分的主题,此外它还包括RSA密码系统、整数乘法的分治算法、排序与寻找中项以及快速Fourier变换。本书还包含其他三个部分:其中第二部分堪称本书内容最传统的章节,主要围绕数据结构和图论展开。这一部分中,错综复杂的问题结构和用于解决问题的简洁明快的伪代码形成了鲜明对比。如果希望以传统的方式进行讲授,可以直接从本书的第二部分开始,这部分自成体系(在序言之后),如有需要,可再跳回第一部分。在本书的第一和第二部分,我们介绍了某些用于解决特定问题的技术(例如贪心算法和分治技术);第三部分介绍一些强有力的算法设计技术,它们被广泛地用于解决实际问题:如动态规划技术(一种新颖的可用于清除学生的传统学习障碍的方法)和线性规划技术(一种简洁而直观地处理单纯形法、对偶问题以及原问题的简化问题的技术)。本书最后的第四部分介绍了对付困难问题的方法:NP完全性、各种启发式算法以及量子算法,后者或许是当今最前沿的课题。碰巧的是,我们关于算法的讲述在本书的末尾又回到了最初讨论的问题:针对因子分解问题的Shor量子算法。
本书包含了三个附加的脉络。为了保持全书的可读性(兼顾学生的不同需求和兴趣)和逻辑的完整性,它们以三组自成系列的“灰色方框”形式出现,分别对应于一些算法技术的历史背景、对所介绍算法如何在实际中应用(突出了互联网应用)的描述,以及对相关数学知识的简要阐释。
我们的很多同事为此书的出版做出了重要贡献。在此对Dimitris Achlioptas、Dorit Aharanov、Mike Clancy、Jim Demmel、Monika Henzinger、Mike Jordan、Milena Mihail、Gene Myers、Dana Randall、Satish Rao、Tim Roughgarden、Jonathan Shewchuk、Martha Sideri、Alistair Sinclair,以及David Wagner表示由衷的感谢,他们均对本书提出了宝贵意见,并对本书的初稿作了校对。Satish Rao、Leonard Schulman和Vijay Vazirani对本书几个核心章节的内容给出了重要建议。Gene Myers、Satish Rao、Luca Trevisan、Vijay Vazirani和Lofti Zadeh提供了本书的习题。最后,向加州大学Berkeley分校和San Diego分校的同学们表示感谢,是他们推动了本书的出版工作,并参与审阅本书的手稿。
目 录
第0章 序言1
0.1 书籍和算法1
0.2 从Fibonacci数列开始3
0.3 大O符号6
习题9
第1章 数字的算法13
1.1 基本算术13
1.1.1 加法13
1.1.2 乘法和除法16
1.2 模运算18
1.2.1 模的加法和乘法21
1.2.2 模的指数运算21
1.2.3 Euclid的最大公因数算法23
1.2.4 Euclid算法的一种扩展24
1.2.5 模的除法27
1.3 素性测试28
1.4 密码学35
1.4.1 密钥机制:一次一密乱码本和AES36
1.4.2 RSA38
1.5 通用散列表40
1.5.1 散列表41
1.5.2 散列函数族41
习题44
第2章 分治算法53
2.1 乘法53
2.2 递推式57
2.3 合并排序59
2.4 寻找中项62
2.5 矩阵乘法66
2.6 快速Fourier变换67
2.6.1 多项式的另一种表示法68
2.6.2 计算步骤的分治实现71
2.6.3 插值75
2.6.4 快速Fourier变换的细节78
习题83
第3章 图的分解93
3.1 为什么是图93
3.2 无向图的深度优先搜索96
3.2.1 迷宫探索96
3.2.2 深度优先搜索99
3.2.3 无向图的连通性100
3.2.4 前序和后序100
3.3 有向图的深度优先搜索101
3.3.1 边的类型101
3.3.2 有向无环图103
3.4 强连通部件105
3.4.1 定义有向图的连通性105
3.4.2 一个有效的算法106
习题110
第4章 图中的路径119
4.1 距离119
4.2 广度优先搜索120
4.3 边的长度122
4.4 Dijkstra算法123
4.4.1 广度优先搜索的一个改进123
4.4.2 另一种解释127
4.4.3 运行时间129
4.5 优先队列的实现129
4.5.1 数组129
4.5.2 二分堆130
4.5.3 d堆131
4.6 含有负边的图的最短路径131
4.6.1 负边131
4.6.2 负环135
4.7 有向无环图中的最短路径135
习题136
第5章 贪心算法143
5.1 最小生成树143
5.1.1 一个贪心方法144
5.1.2 分割性质146
5.1.3 Kruskal算法147
5.1.4 一种用于分离集的数据结构148
5.1.5 Prim算法153
5.2 Huffman编码156
5.3 Horn公式160
5.4 集合覆盖162
习题164
第6章 动态规划173
6.1 重新审视有向无环图的最短路径问题173
6.2 最长递增子序列175
6.3 编辑距离177
6.4 背包问题183
6.5 矩阵链式相乘186
6.6 最短路径问题189
6.7 树中的独立集193
习题195
第7章 线性规划与归约205
7.1 线性规划简介205
7.1.1 示例:利润最大化206
7.1.2 示例:生产计划210
7.1.3 示例:最优带宽分配212
7.1.4 线性规划的变体214
7.2 网络流216
7.2.1 石油运输216
7.2.2 最大流216
7.2.3 对算法的深入观察217
7.2.4 最优性的保证221
7.2.5 算法的效率222
7.3 二部图的匹配222
7.4 对偶224
7.5 零和博弈(游戏)228
7.6 单纯形算法232
7.6.1 n维空间中的顶点和邻居232
7.6.2 算法233
7.6.3 补遗236
7.6.4 单纯形法的运行时间238
7.7 后记:电路值241
习题243
第8章 NP-完全问题253
8.1 搜索问题253
8.2 NP-完全问题264
8.3 所有的归约268
习题286
第9章 NP-完全问题的处理293
9.1 智能穷举搜索294
9.1.1 回溯294
9.1.2 分支定界297
9.2 近似算法299
9.2.1 顶点覆盖300
9.2.2 聚类302
9.2.3 TSP304
9.2.4 背包问题306
9.2.5 逼近的层次307
9.3 局部搜索中的启发方法308
9.3.1 重新审视旅行商问题308
9.3.2 图划分311
9.3.3 处理局部最优313
习题316
第10章 量子算法321
10.1 量子位元、叠加状态和度量321
10.2 算法设计325
10.3 量子傅立叶变换327
10.4 周期性329
10.5 量子电路331
10.5.1 基本量子门331
10.5.2 量子电路的两种基本类型332
10.5.3 量子傅立叶变换电路333
10.6 将因子分解问题转化为周期求解问题335
10.7 因子分解的量子算法337
习题339
历史背景及深入阅读的资料343