读书笔记—《算法设计与分析基础》
http://lib.csdn.net/article/datastructure/31526
最近粗读了一本算法基础书,看有好多人推荐就学习学习,感觉书写的还是不错的,不敢说学到了多少新算法,至少拓展了视野,系统了知识,本书中确实有不少以前没见过没听过的算法,这些算法不一定会直接用于实际实践中,但是对思路的拓展肯定有不小帮助,而且最好的地方时,正如书中所说,写的确实很浅显易懂。
粗读了一遍,课后题也没有认真完成(实在耐不下性子),但感觉还是有必要先总结一下,以备日后再回头看。
有两种思想,像珠宝商放在天鹅绒上的宝石一样熠熠生辉,一个是微积分,另一个就是算法。微积分以及在微积分基础上建立起来的数学分析体系造就了现代科学,而算法则造就现代世界。——David Berlinski
本书的目的是:使读者了解计算领域中不同的问题的一系列标准算法;此外,还要具备设计新算法和分析其效率的能力。
学习算法的第一步,也是最重要的是要理解问题,否则你极有可能要返工。
一、算法求解基本步骤:
1.理解问题:最基础也是重要的一步,决定着算法的成败
2.了解计算设备的性能:根据不同计算设备的性能往往需要不同的算法策略(顺序算法、并行算法)
3.在精确解法和近似解间做选择:对于一些无法在目前或者在时间限制内找到精确解法的问题,我们就必须在两者之间做出选择
4.确定适当的数据结构:我们都知道程序=算法+数据结构
5.选择恰当的算法:即根据问题和数据结构本身选择恰当的算法,可以是成熟的,也可以是自己开创的
6.详细的表述算法:伪代码是一个不错的选择
7.证明算法的正确性:不光是算法本身的正确性,还必须有“有限时间内”这个条件限制
8.分析算法:时间效率、空间效率角度去研究和分析改进算法
9.实现算法并调试
二、重要的问题类型
排序;查找;串处理;图问题;组合问题;几何问题;数值问题;
三、基本数据结构
线性数据结构(数组和链表);图;树;集合与字典;
四、算法效率分析
1.常用符号
● O(读作“哦”):表示算法增长次数小于等于其参数函数的增长次数
● Θ(读作“theta”):表示等于
● Ω(读作“omega”):表示大于等于
算法的效率常见排序:常数(少有的极优秀的算法) > logn > n > nlogn > n^2 > n^3 > 2^n > n!
2.算法的分析方法
● 数学分析
● 经验分析
● 可视化分析
3.算法的效率分类
● 最优效率:即算法在最理想的情况下能够达到的效率
● 最差效率:即算法在最差的情况下达到的效率
● 平均效率:顾名思义
● 分摊效率:有些算法有这样的特性,随着算法运行的次数越多,算法效率会有变化,所以要考虑算法的平摊效率
==================================================================
算法的几大分类和简要说明:
一、蛮力法
就像宝剑不是撬棍一样,科学也很少使用蛮力。——Edward Lytton
蛮力法:一种简单直接地解决问题的方法,常常直接基于问题的描述和所涉及的概念定义。(蛮力法虽然有时看着特别傻,但是他的确是应用范围最广的一类算法,事实上,他可以算是能够几乎解所有问题的一般性方法)
蛮力法在个别情况下效率还是比较理想的,我们可以把他作为“保底”算法,或者把他当做准绳来衡量和改进同类算法,以得到更高效的算法。(应用蛮力法得出的第一个算法,往往可以通过改进来提升他的性能)
蛮力法的主要实例:
● 选择排序
● 冒泡排序:(如果不是因为他有一个好记的名字,我们很可能不会对他有任何了解)
● 模式匹配:(指采用蛮力法进行模式匹配)
● 最近对和凸包问题的蛮力算法
● 穷举:TSP、背包、分配问题(事实上,可能不能在有效时间内得到结果)
言而总之,总而言之,蛮力法虽然名字不好,但是有时不失为一种恰当的决策(在衡量算法开发时间和效率时,开发时间比较重要时),或者作为研究一个问题的开端,逐步改进以得到更好的算法。
二、分治法
分治法:将问题划分为几个规模尽量相似的子类,然后对较小的子类问题求解(一般采用递归来分解),然后合并子类以求解原始问题。
分治法可算是最著名的通用算法设计技术了(蛮力是解决,此处是设计),很多非常有效的算法实际上是他的特殊实现。
分治法实例:
● 合并排序
这个算法的时间效率在任何情况下都是nlogn,主要缺点是需要相当大的额外空间。
● 快速排序
一种较为出众的nlogn算法,而且他的最差效率是平方级的。(快排在对付随即产生的数据时,要比大部分排序算法更有优势,对基本有序的可能做许多无效功)
● 折半查找
● 大整数乘法和Strassen矩阵乘法
● 用分治法解决最近对和凸包问题
三、减治法
减治法利用了一种关系:一个问题给定实例的解和同样问题较小实例的解之间的关系。
三种主要变种:
● 减去一个常量
这个其实就是f(n)=f(n-1)+常数,减去的这个常量也往往是1。
● 减去一个常数因子
意思是问题规模的缩小可能是每次缩小一倍,或者开方,总之是按照一种固定的规模缩减。
● 减去的规模是可变的
问题规模的缩小是变化的,例如欧几里得算法(求最大公约数的经典解法):gcd(m,n)=gcd(n, m mod n)
我认为本章中有一个例子比较常用(或者说我就看懂了这一个。。。呵呵),就是生成具有N个元素集合的子集问题:
● 一种是从下到上生成
第一次:空
第二次:空 a1
第三次:空 a1 a2 a1+a2
第四次:空 a1 a2 a1+a2 a3 a2+a3 a1+a2+a3
看出来了吗?每次都是上一次的集合加上上一次集合加上一个新元素。
● 另外一种是比较简单,但是以前一直没想到过
就是用一个n位二进制数代表集合,每位是1或0则表示包不包含该位的元素,这样从0开始每加一,便生成了一个子集(用这种方法,往往会要求按照挤压序生成)
四、变治法
变治策略:问题的实例-->更简单的实例、另一种表现或者另一个问题的实例-->解
该思想的实质是把一个新问题转化成一个有成熟算法的老问题进行求解,用到三种方法:实例化简,改变问题的表现形式,问题化简
常见的主要实例有:
● 把查找问题转化为排序问题
● 高斯消去法:即把多项式方程的系数矩阵进行行变换,化成上三角矩阵再从底向上求解(改变了问题的表现形式,即系数的表现形式)
● 堆排序,将排序问题转化对完全二叉树的分析
● 计算机应用中把乘法的运行次数降低(通过添加加法,或者记录每次的乘积结果例如霍纳法则)
五、时空权衡
最重要的事情用药不能受次要事情的支配。——Johann Wolfgang von Goethe
时空权衡主要是指在某些情况下时间会成为决定因素或者空间会成为决定因素时,我们需要牺牲某一部分来换取另一部分效率的提高。常用的方法有输入增强(即对输入做一定的处理如排序等)和与构造(对输入数据的结构进行一定的处理)
常见的实例有:
● 计数排序
● KMP模式匹配算法、Horspool算法
● 散列法(对问题的结构进行了预处理):哈希散列,开散列,闭散列
六、动态规划
如果问题是由交叠的子问题所构成的,我们就可以用动态规划技术来解决它。一般的方法是把之前的运算结果记录,然后后续运算利用之前的结果集来进行运算,最后得出解。
常见的实例有:
● 背包问题
● Warshall算法、Floyd算法
● 最优二叉查找树
总之,就是常用来解决一些涉及最优解的问题。
七、贪婪算法
贪婪,我找不到一个更好的词来描述它,它就是好!它就是对!它就是有效!——美国演员迈克●道格拉斯
每次选择时都尽量选择最符合问题的路径。
每一步必须符合的原则是:
● 可行的:必须满足问题的约束
● 局部最优:当前可行步骤中的最佳选择
● 不可取消:一旦做出选择,在算法的后面步骤就无法改变
常见的实例:
● Prim算法:基于选择最小路径
● Kruskal算法:基于选择最小边(他在发明该算法时才研二!)
● Dijkstra算法:单起点最优,每次都选择离起点最近的路线
● 哈夫曼编码:最小前缀的应用(他是在作课后作业是发明的!)
==================================================================
本书中的章节大致如上总结,总结的有地方简单有的地方粗糙,甚至不正确(毕竟我水平也不咋的),总结总结大家分享讨论,共同提高。
最后我还想说几点就是,算法千万不要把他简单的看做是某个领域内的求问题的方法,我的观念是,算法是一种哲学,一种你可以用来解决任何问题的方法,学习算法的过程中我对人生也有了许多感悟,总之越来越喜欢算法哲学了(虽然喜欢的好辛苦)。还有就是,书中一些算法的发明者往往都是在校大学生,这点让我十分惭愧,十分十分惭愧!