1. 基本定义
算法(Algorithm)是指用于解题问题的具体的方法。在计算机领域,算法是指一系列明确的操作的组合以根据指定的输入计算出所需要的结果。广义来说,用于解决问题的一系列的方法或步骤都可以称之为算法。比如制作一个汉堡,从准备材料到加工的整个过程,就是一个算法。
2. 算法的主要特性
关于算法特性的描述,一般采纳的都是高德纳(Donald E. Knuth)的著作《计算机程序设计艺术》的 1.1 算法
中,对算法特征进行了以下描述。算法主要有五大特性:
- 有限性:一个算法必需在有限的步骤内完成。
- 确定性:算法中对各步骤的内容的描述必须精确无歧义。
- 输 入:一个算法必须有零个或多个输入。
- 输 出:一个算法应有一个或以上输出,输出量是算法计算的结果。
- 可行性:即最终是可以实现并按要求解决问题的。
以KFC制作汉堡为例,制作汉堡的算法由多个制作步骤组成,每个步骤都有相应的参数(时间、数量、配比等),对应以上五点分别表示以下内容:
- 有限性:汉堡必需在有限步骤内完成。
- 确定性:每个步骤都有明确无歧义的说明,从而保证无论是哪个操作员工,都应该能够制作出相同的汉堡。
- 输 入:汉堡的所有原料,面包、馅、肉、配料等。
- 输 出:包装好可食用的汉堡。
- 可行性:根据汉堡的制作算法,一定能够以可接受的时间,可接受的成本,制作出来大家认可的健康食用的汉堡。
3. 算法质量判定方法
判定算法的好坏有两个要求:
- 时间复杂度:指算法所需要消耗的时间资源,不同的算法达到相同的结果会有巨大的差异,这个后面我们会看到。
- 空间复杂度:指算法所需要消耗的空间资源。在计算机中,主要是指内存资源。
仍然以做汉堡为例,
- 时间复杂度:指制作汉堡所需要的时间,在KFC中,所有的材料都是半成品的,所以可以快速的制作完成。
- 空间复杂度:指制作汉堡所需要的场地和设备,KFC采用半成品的方式,只需要按顺序简单的原料组合和加热,就可以完成,不需要太多的场地。
以上的定义和示例只是让大家快速理解这两个概念,现在我们看几个计算机中真实的示例,以理解以上的两个概念。
3.1. 时间复杂度
先看一个问题:给定数列$D = [d_1, d_2, ..., d_n]$,判断是否存在数据$v$,如果存在返回在数列中的位置,不存在返回-1。
【未排序】 在求解的时候,如果数据是未排序的,我们只能一个一个对比,从 $d_1$ 一直对比到 $d_n$。我们可以很容易发现:
- 最好的情况是 $d_1$ 就与 $v$ 相同,则只需要1次查找;
- 最坏的情况是最后一个数 $d_n$ 是,或者压根就不存在,则需要查找 $n$ 次。
由于所有的数字都是平均分布,所以平均查询次数 $t = \frac{1 + n}{2} = \frac{1}{2}n + \frac{1}{2}$次。这个表达式属于一次多项式 $p = an + b$,其中 $a$ 和 $b$ 的值都是 $\frac{1}{2}$。由于计算次数主要受 $n$ 的影响,所以为了更简单地表述算法的时间复杂度,我们将常量省略,并用大写的O表示,即此算法的复杂度为 $O( \frac{1}{2}n + \frac{1}{2}) = O(n)$。
【已排序】 如果是已经排序(假设自左向右递增)的话,我们利用折半查询,从而大大加快查询过程。具体算法为:
- 我们从中间开始对比,如果中间的数比 $v$ 小,则我们只需要在 $D$ 的左半边查找,反之在 $D$ 的右半边查找。
- 重复这个过程直到找到 $v$ 或 折半后的宽度长度为1.
对比两种算法,如果对于已经排序好的数列长度为10亿的数列,采用第一种算法,则最坏需要进行约10亿次查找;而采用折半法的最坏查询次数为 $O(log_2n)$,比如 长度为约为10亿时,只需要30次($2^{30}=1,073,741,824$)折半查询的即可完成查找。
所以,通过以上的示例可以看出,对于相同的输入和计算要求,由于算法不同,时间复杂度会有很大的不同($O(n)$ VS $O(log_2n)$),从面导致时间消耗上的巨大差异。
3.2. 空间复杂度
空间复杂度也是一样。以图片保存为例,一张没有压缩的1920x1080的图片,共有 2,073,600个点,每个点占4个字节(ARGB表示),则共需要占用8,294,400个字节,约占 7.9MB的磁盘空间。而采用了压缩算法以后,实际容量往往只有几百KB。
3.3. 时空转换
爱因斯坦的相对论告诉我们,在我们生活的这个世界中,时间和空间是可以转换的。同样的,在算法中,时间和空间也是可以相互转换的,即我们可以用时间换空间或者用空间换时间。具体是如何工作的,请看以下的示例。
- 时间换空间 如影片的压缩。大家都听过,原始的影片的大小非常大,通过压缩算法可以大在减小影片文件的大小,但是压缩和解压都是需要时间的。影片在播放的时候,是在读取后,先进行解压才能进行播放,只是解压的时间非常短,让我们感觉不到,所以我们感觉是实时在播放。实际上,这就是一个用时间换空间的很好的示例,通过压缩和解压的实时计算,减少了磁盘空间的占用。
- 空间换时间 在天文学计算的时候,经常会用三角函数,进行天体运行的计算。但是由于在计算中,sin(x) 会利用泰勒公式展开计算(当然,实际上有一些优化以加快计算速度),每次计算然需要几个微秒的时间。但是当计算量很大的时候,仍然会占用非常可观的时间。为了解决这个问题,我们只需根据精度,建立一个数列,比如以0.000000001度为精度,可以生成 360,000,000,000个的数据。虽然占用了非常大的内容空间,但是由于数列是顺序排列的,查询可以在几个纳秒内完成,运算效率可以提高上千倍,极大地加速了运算效率。
4. 程序的质量指标
- 正确性:程序的最基本要求,任何程序都是建立在正确性之上
- 强壮性:程序有好的容错机制。
- 高性能:单时间内有较高的数据处理量。
- 省存储:尽可能少地占用存储空间。
- 易读性:算法的表示是否容易被人所阅读和理解。(1.单行单语句 2.同级对齐 )
4.1. 正确性
算法最基础的要求,如果计算错误,则算法的价值为0 由于大部分算法的复杂性,保证正确很困难 大量的测试方法都是为了保证算法的正确性 常用测试方法 黑盒测试、白盒测试、灰盒测试 手动测试、自动化测试 单元测试、集成测试、整体测试 静态测试、静态测试 开发测试 、用户测试 其他测试: (1)回归测试(regression testing)是指对软件的新的版本测试时,重复执行上一个版本测试时的用例。 (2)冒烟测试(smoke testing),是指在对一个新版本进行大规模的测试之前,先验证一下软件的基本功能是否实现,是否具备可测性。 (3)随机测试(random testing),是指测试中所有的输入数据都是随机生成的,其目的是模拟用户的真实操作,并发现一些边缘性的错误。
4.2. 强壮性
- 强壮性:程序有好的容错机制。
4.3. 高性能
单时间内有较高的数据处理量。
算法的效率问题图书馆数据库有100万条图书信息,由于工作人员疏忽,在数据录入的时候产生了几条重复的信息,为了避免错误出现,现在要求通过编程将重复信息去除。
4.4. 省存储
尽可能少地占用存储空间。
对是数据库类程序尤其重要例如:网络搜索企业公司客户信息
4.5. 易读性
算法的表示是否容易被人所阅读和理解。(1.单行单语句 2.同级对齐 )
编程有很大一部分时间是在阅读代码,不仅要阅读自己的代码,而且要阅读别人的代码。因此,可读性良好的代码能够大大提高编程效率。可读性良好的代码往往会让代码架构更好,因为程序员更愿意去修改这部分代码,而且也更容易修改。只有在核心领域为了效率才可以放弃可读性,否则可读性是第一位。
软件的规模越来越大,一个系统通常需要几代程序员来开发维护。然而这些程序员们往往都素未谋面,只能“神交”于代码的字里行间。可见,代码的可读性的重要性,也是一个优秀程序员的标志。---《重构》
在程序开发的大流中,相信每个人都曾吐槽过他人代码的难以理解,设计之差,然而一味吐槽他人而忽略自身的问题也将走入另一个误区,也就是忽视自身理解力的提高。因此,在我们努力提升自身代码可读性的同时,同样也要提升自身的理解力,多读他人代码,吸取他人经验教训,才能成长的更快!写得出好代码,也看的懂烂代码(如果过于烂则另当别论),才是高手!
5. 算法类型
两种算法设计的基本模型
结构化方法,自顶向下问题逐步细化的设计方法
面向对象方法,以抽象化,封装性,多态,继承为特性的设计方法。
6. 算法的实现
数据类型和结构的选择 计算过程 输出结果 测试,再测试
6.1. 数据类型和结构的选择
根据不同的数据类型选择相应的存储类型, 例如:人数, 物品个数,一般用整型;物理量, 数学值 一般用浮点型等. 对于复杂的数据结构要专门建立数据结构. 例如:图书馆数据库里的一条图书记录
6.2. 计算过程的差异
由于计算采用的是2进制,所以位数不同,处理顺序不同都可能会造成误差 由于各种语言的优先级不同,也会有差异 对于数据是寻值还是寻址的差异 书写上的一些差异
6.3. 输出结果
现代的程序设计已经完成把数据和结果显示进行分离.对程序分为数据层和UI(User Interface)
数据层负责数据的保存
UI层,负责调用数据层的数据进行显示
6.4. 不断测试提高质量
好的程序是改出来的! 任何一个程序都需要大量的测试,BUG的修正, 和不断的升级和补丁才会变成一个完美的程序! 好的程序一般都是补丁型的
7. 总结
本文对算法的基本概念做了一个定性的介绍。在后面的内容中, 将进行更加深入的定性分析。
8. 参考文献
[1] 数据结构, https://www.cnblogs.com/jingcaijueyan/p/9456072.html [2] 数据结构知识整理, https://blog.csdn.net/m0_37568814/article/details/81288756