概念
什么是算法?英文维基百科给出了这样的定义:“An algorithm is a finite sequence of well-defined, computer-implementable instructions, typically to solve a class of problems or to perform a computation.”翻译过来的意思就是一系列有限的、清晰定义的、可实现的计算机指令,并用以解决一类问题或进行计算。
哈哈,别被这么官方的定义搞懵了。在生活中一个最简单的类比就是烹饪。如果把要烧一道菜比作这里要解决的问题,那么菜谱中每一道工序,每一个步骤就是这道菜的“算法”了。但这样的类比还存在一些问题:我们的算法要求每一步都是要明确定义的,也就是说不能有任何歧义。以炸鸡腿为例,如果菜谱上是这样描述的:
- 将油倒入锅中加热至七成热
- 把鸡腿放入油锅中炸至金黄
- 放入少许盐后出锅
以上的步骤虽然前后有逻辑性,最终能够做出炸鸡腿,但是这样的定义还是不符合算法的规范的,因为这样的定义只具备了经验性(empirical),不具备系统性(systematical),计算机是读不懂人类的这些经验的。所以,我们将这些步骤稍加修改一下:
- 将 200ml 的色拉油放入锅中,打开电炉,调至第 9 档,当油温达到 200°C 后停止加热
- 把碗里的鸡腿依次放入油锅中煎炸,观察鸡腿的颜色,直至鸡腿表面呈现出 #FF9900 (金黄色 RGB 的十六进制表示法)
- 根据鸡腿的重量(g)和一个函数 f(x) 计算出需要放入的盐的重量(g),将其放入锅中。两分钟后取出油锅中将鸡腿放入一个空碗中
这样我们的计算机能读懂菜谱啦~当然这只是举个例子,真正的炒菜机器人内部的算法比这个复杂得多,只是想告诉大家,算法是一个抽象到具体的过程,每一步都需要明确定义。
解决的问题
了解完了算法的概念,那么算法到底是用来干什么的呢?它可以用来解决一系列问题,这样的问题可以是排序, 查找,组合问题 等等。这类问题一般都有很多种解决的方式,我们只需要找到一种解决了即可。
但是,仅仅找到了算法来解决这些问题是远远不够的。我们还需要考虑这个算法的效率如何,也就是对其进行复杂度分析,这里暂时不做讨论。先举一个经典的算法问题:扔鸡蛋问题。问题的描述为假设楼高为 100 层,给你 2 个鸡蛋,你需要试出来从第几层往下扔鸡蛋刚好会碎,也就是说如果第五层鸡蛋没碎,第六层碎了,那么在第六层扔鸡蛋刚好会碎。你可能首先想到的就是从第一层开始一层一层地往上扔直到鸡蛋被摔碎,但是这样你可能最多扔 99 次(假设从 100 层往下扔鸡蛋才碎),而且你没有利用上第二个鸡蛋。聪明一点的同学可能又想到了用一个鸡蛋十层十层地往上扔,如果碎了就在两个十层之间一层一层地往上扔。这样最多可能要扔 19 次,已经比之前的方法有了明显的进步,这就是算法要考虑的效率问题。事实上,这个问题的最优解是最多扔 14 次,需要用到后面要讲到的动态规划。
例子
那么要怎样来具体地描述一个算法呢?让我们看一道完整的例子,求两个数的最大公约数。相信在中学阶段老师就告诉过你把两个数的因子提取出来,然后把所有共同的因子相乘得到结果。这里我们对这种方法不做讨论,让我们先看看一种更高效的解法:欧几里得算法(Euclid’s algorithm)。
欧几里得算法又叫辗转取余法,其原理是两个数的最大公约数等于较小数与两数取余的最大公约数,然后重复前面的过程,直到两个数中出现 0 就返回。这个过程看似简单,但要用算法语言来表示却不容易。算法语言可以是自然语言,也可以是数学语言(伪代码),让我们先以自然语言为例吧。
- 第一步:如果 n = 0,返回 m;否则执行第二步。
- 第二步:m 除以 n,将余数赋给变量 r。
- 第三步:把 n 赋给 m,r 赋给 n;执行第一步。
如果用伪代码来表示的话就是这样的:
→ 本节全部代码 ←