1.0 前言
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。
简单地说,算法其实是解决问题的方法,他不是属于计算机方面的一个专属名词。不正式的说,算法是任何定义明确的计算过程,比如买菜大妈的买菜策略,骗子的坑人步骤,再比如高考中数学的立体几何,有的人写二十行,有的人写四十行,他们都写上了属于自己的算法,但是效率不同。
复杂一点的,再比如:一个环卫工人要清扫五个地点:
她也有不止一种执行完这个任务的方法:
那到底哪种方法走的路比较少?这就是我们之后要学习的最短路算法。
下面举一个例子来了解算法:
我们从小都见过的小学奥数题:有一口井七米深,一只青蛙从井底爬上来,白天爬了三米,晚上掉下两米,这样它要爬几天才能上来?
我们最笨的解题步骤
- 让青蛙爬三米
- 判断是否爬上去了
- 让青蛙掉下两米
- 重复上述过程并记录天数
(而不是计算得出一天爬一米,得出七天爬上来)
或者另一种算法:
- 最后一天直接爬上去:7-3=4米
- 其余天,一天一米:路程/速度=4天
- 4+1=5天
由此可见,我们首先要保证算法的正确性,其次要保证算法的效率,我们比较一下上述算法:
- 计算得出一天爬一米,得出七天爬上来,这种算法是错误的
- 模拟法,让青蛙模拟整个爬井的过程,我们看看有几次运算:
0+3=3,3<7
3-2=1
次数:0+1=1
1+3=4,4<7
4-2=2
次数:1+1=2
0+3=3,5<7
5-2=3
次数:2+1=3
0+3=3,6<7
6-2=4
次数:3+1=4
4+3=3,7=7,爬出来了
次数:4+1=5
一共进行了19次运算
- 公式法:7-3=4,3-2=1,4/1=4
进行了3次运算
从这个对比中应该能感受到算法不同,效率差距是很大的,然而感受其实的还不够。
试想:
假如这口井是100米?1000米?10000米?随着深度的增加,模拟法的计算次数也再增加,和深度成正比。
而再看公式法,其实和深度并没有什么关系,不管深度是多少,公式法永远只用计算3次即可。
这种计算次数和条件的大小,数量等因素没有关系的算法,视为非常优秀的一种算法。
那我们计算机的算法到底是什么?我们提到,算法不是计算机领域才拥有的概念,它可以用任何方式来描述,我们之前接触到的基本都是用文字来表达,比如我在上文中列出的解题步骤。而我们要干的事,或者说计算机的算法要干的事,就是用代码来描述计算机上的处理过程。我们用一行行的语句来描述一个个解决问题的方法,这就是编程。
计算机所有算法都是通过基本语句一点点描述出来的,比如上文的模拟算法:
- 让青蛙爬三米
- 判断是否爬上去了
- 让青蛙掉下两米
- 重复上述过程并记录天数
其中涉及到编程的很多基本语句:加减法、条件判断语句、循环语句等,编程语言也有很多,但是只是一个工具而已,就和汉语英语一样,并且单词数量比这两种语言少很多。所以只要脑海中的思路足够清晰,我们用一种(编程)语言把它描述出来即可,而不是我们之前去用汉语描述。
事实上,计算机的算法解决的问题可能是更加实际的问题,关系着我们每个人的生活,比如地图app如百度地图,高德地图,是如何帮你规划的路线的?再比如双十一如此疯狂的购物,各大购物网站是如何保证不会崩溃的?再比如到现在还没解决的问题:微博到现在为止,志玲姐姐结个婚,服务器都会崩。
如何衡量一个算法到底优不优秀?我们下篇来讲。
1.1何为算法
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。
如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。
下面为算法的五个特性:
有穷性(Finiteness)
算法的有穷性是指算法必须能在执行有限个步骤之后终止;
确切性(Definiteness)
算法的每一步骤必须有确切的定义;
输入项(Input)
一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;
输出项(Output)
一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
可行性(Effectiveness)
算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步,即每个计算步都可以在有限时间内完成(也称之为有效性)。
1.2衡量算法的指标
一个算法的优劣可以用空间复杂度与时间复杂度来衡量。
用几句话简单说明一下时间复杂度。
时间复杂度并不是表示一个程序解决问题需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快。也就是说,对于高速处理数据的计算机来说,处理某一个特定数据的效率不能衡量一个程序的好坏,而应该看当这个数据的规模变大到数百倍后,程序运行时间是否还是一样,或者也跟着慢了数百倍,或者变慢了数万倍。
1)不管数据有多大,程序处理花的时间始终是那么多的,我们就说这个程序很好,具有O(1)的时间复杂度,也称常数级复杂度;
2)数据规模变得有多大,花的时间也跟着变得有多长,这个程序的时间复杂度就是O(n),比如找n个数中的最大值;
3)而像冒泡排序、插入排序等,数据扩大2倍,时间变慢4倍的,属于O(n^2)的复杂度。
4)还有一些穷举类的算法,所需时间长度成几何阶数上涨,这就是O(a^n)的指数级复杂度,甚至O(n!)的阶乘级复杂度。
注意:不会存在O(2*n^2)的复杂度,因为前面的那个“2”是系数,根本不会影响到整个程序的时间增长。同样地,O (n^3+n^2)的复杂度也就是O(n^3)的复杂度。
因此,我们会说,一个O(0.01*n^3)的程序的效率比O(100*n^2)的效率低,尽管在n很小的时候,前者优于后者,但后者时间随数据规模增长得慢,最终O(n^3)的复杂度将远远超过O(n^2)。我们也说,O(n^100)的复杂度小于O(1.01^n)的复杂度。
而空间复杂度是相似的,只是他的单位不是时间,而是空间,可以认为是内存。
2.1计算机
说完算法,我们来谈谈计算机吧。
计算机的优势非常明显:速度、记忆、精确,但是它也不是万能的。
就算计算机速度快,也不可以给他无限大的工作量,就算计算速度再快的计算机,在O(N^N)等复杂度的算法面前,也是无能为力,具体使用那种算法是人说了算,计算机无条件执行,这就对编程的人提出了要求。
计算机的容量是非常大的,但是也不能存无限多的东西,同样的问题,解决方法不同,花费的空间差异也是巨大的。
计算机是精确的,但是解决问题的方法和人类不一样,比如计算机很难回答某件物品“美不美”,“值不值”,再比如很多算法对人类是很直观的,可以用语言描述,但是转化成机器语言会花很多时间。
总而言之,请善待你的计算机啦。