我记在大学时,什么都想学。曾跟着老师学修收音机,老师教我修收音机有固定的工序:先查电源、再查天线、后查功放,每一步都有明确的操作,按步骤来再复杂的故障也能定位 ------ 这就是算法的雏形。算法的本质,就是解决特定问题的 "一组明确、有限的步骤",而且这些步骤能被计算机机械执行,不会有任何模糊不清的地方。

比如要计算两个数的最大公约数,不是凭感觉试数,而是按固定的步骤一步步算;要找 100 以内的素数,不是随机猜测,而是按筛法的规则逐个筛选。
很多新手把算法想得太复杂,觉得是 "高深的数学公式",但在我看来,算法的核心就两点:一是 "步骤有限",不能无限循环下去,必须在有限步骤内得到结果;二是 "机械执行",每一步该做什么都有明确规则,不需要人为判断,计算机能照着做。比如我们之前写的 LED 延时程序,其实也包含简单算法:"设置计数器→计数器减 1→不为 0 则重复→为 0 则结束",这就是一个解决 "延时" 问题的简单算法,符合有限步骤、机械执行的特性。
所以第一次接触 "算法" 这个概念,是用汇编语言写 "计算两个数的最大公约数" 程序。当时老师告诉我:"别瞎试,按欧几里得的步骤来,几步就能算出来"------ 这就是后来我才知道的 "辗转相除法",也是最经典的算法之一。所以从那时候起我就明白,好的算法能让复杂问题变简单,让计算机用最少的步骤得到结果,这也是学习算法的核心意义。
一、典型算法
掌握算法,先要吃透几个经典的典型算法,理解它们的逻辑实现 ------ 这也是教学的核心重点。我结合自己的实践,聊聊最常用的三个算法:辗转相除法(求最大公约数)、埃拉托斯特尼筛法(找素数)、线性搜索(找数据),以及线性搜索里的 "哨兵技巧" 这个经典优化方法。
1. 最大公约数
辗转相除法,也叫欧几里得算法,核心逻辑是 "用较大数除以较小数,得到余数,再用除数和余数重复这个过程,直到余数为 0,此时的除数就是最大公约数"。比如计算 24 和 18 的最大公约数:
- 24÷18=1 余 6
- 18÷6=3 余 0
- 余数为 0,所以 6 就是最大公约数
这个算法利用了 "两个数的最大公约数等于较小数和余数的最大公约数" 的数字规律,步骤少、效率高。
我年轻时用 Z80 汇编实现过这个算法,核心代码逻辑很清晰:先把两个数分别存入 HL 和 DE 寄存器对(假设 HL=24,DE=18),然后进入循环:用 HL 除以 DE 得到余数,把 DE 的值赋给 HL,余数赋给 DE,判断 DE 是否为 0,不为 0 则继续循环,为 0 则 HL 里的数就是结果。
当时对比过 "试除法"(从较小数往下试,能同时整除两个数的就是最大公约数)和辗转相除法,试除法算 24 和 18 要试 18、17...... 直到 6,而辗转相除法只要 2 步,效率差距一目了然。这让我明白,算法的优劣直接决定程序效率,好的算法能充分利用数字规律,减少计算步骤。
2. 埃拉托斯特尼筛法
埃拉托斯特尼筛法,这是找一定范围内素数的经典算法,核心逻辑是 "先列出所有数,然后从 2 开始,把每个素数的倍数都筛掉,剩下的就是素数"。比如找 10 以内的素数:
- 列出 2-10 的数
- 筛掉 2 的倍数(4、6、8、10)
- 筛掉 3 的倍数(9)
- 剩下的 2、3、5、7 就是素数
这个算法的关键是 "批量筛除",利用 "素数的倍数一定不是素数" 的规律,避免了逐个判断 "是否能被整除" 的繁琐。
我年轻时用这个算法找 100 以内的素数,一开始用笨方法:对每个数,从 2 到它的平方根逐个试除,判断是否为素数;后来改用筛法,代码行数少了一半,执行时间也缩短了很多。比如找 100 以内素数,试除法要对每个数做多次除法,而筛法只需要两次循环(一次列数、一次筛除),充分利用了计算机 "擅长重复简单操作" 的特点 ------ 这也是算法设计的核心要点之一:利用计算机的速度优势,把复杂判断转化为简单重复操作。
3. 数据查找
线性搜索算法,这是在一组数据里找目标值的最基础算法,核心逻辑是 "从第一个数据开始,逐个和目标值比较,找到则返回位置,找不到则返回不存在"。比如在数组 [5,8,3,9,2] 里找 9,就从 5 开始,依次比较 8、3、9,找到后返回位置 4(从 1 开始计数)。线性搜索的逻辑简单,但效率不高,尤其是数据量大的时候,最坏情况要遍历整个数组。
这里要重点讲教学里的优化重点:哨兵技巧。传统线性搜索有个问题:每比较一次,既要判断 "是否等于目标值",还要判断 "是否遍历到数组末尾",比如循环条件是 "i ≤ 数组长度 且 当前值≠目标值",两次判断会增加执行时间。而哨兵技巧的优化思路是:在数组末尾提前放入目标值作为 "哨兵",然后只需要判断 "当前值是否等于目标值",找到后再判断是不是哨兵 ------ 如果是,说明没找到;如果不是,就是目标位置。
我举个汇编实现的例子:假设数组存在内存 0100H-0104H(数据 5、8、3、9、2),要找的目标值是 9。传统方法:i 从 0100H 开始,循环条件是 "i ≤ 0104H 且 (i)≠9";哨兵技巧:先把 0105H(数组末尾下一个地址)存入 9 作为哨兵,然后 i 从 0100H 开始,循环条件只有 "(i)≠9",找到后如果 i=0105H,说明没找到,否则就是目标位置。
这个技巧减少了每次循环的判断次数,数据量越大,优化效果越明显。我年轻时做过测试,用哨兵技巧的线性搜索,执行时间比传统方法减少了约 20%------ 这就是编程优化的价值,不是改变算法核心,而是在细节上减少冗余操作。
二、算法设计的核心要点
从这些经典算法的实践中,我总结出三个算法设计的核心要点,也是新手最需要掌握的:
1. 充分利用计算机的速度优势
计算机不怕重复简单操作,就怕复杂判断。比如埃拉托斯特尼筛法,虽然要循环筛除倍数,但都是简单的赋值和比较操作,计算机能快速完成;而如果用人工的 "试数法",计算机反而效率低。我年轻时写算法,总想着 "少写几行代码",后来发现,只要步骤简单、重复,哪怕代码行数多一点,计算机执行起来也更快。
2. 吃透数字规律,简化计算步骤
好的算法都离不开对数字规律的利用,比如辗转相除法利用 "余数与最大公约数的关系",筛法利用 "素数倍数的规律"。新手写算法容易 "凭感觉试",比如求最大公约数时从大到小试数,而不是用规律简化步骤。我建议新手写算法前,先在纸上推导数字规律,比如先算几个例子,找到规律再写代码,比盲目写程序高效得多。
3. 先在纸上设计算法,再写代码
这是我一辈子坚持的习惯,不管多简单的算法,先在纸上画出步骤、列出例子,再动手写汇编或高级语言代码。比如写鸡兔同笼的算法,先在纸上列出暴力求解和优化的步骤,再转化为代码,能避免代码逻辑混乱。我年轻时吃过亏,直接上手写代码,结果发现算法逻辑有问题,又要回头改,反而浪费时间。
三、暴力求解与优化
为了把算法设计和优化的思路落地,我们以经典的 "鸡兔同笼" 问题为例,分析暴力求解和优化算法的逻辑实现 ------ 这是新手理解 "算法优化" 的最佳案例。问题描述:鸡和兔共 35 只,脚共 94 只,求鸡和兔各多少只?
1. 暴力求解算法
核心逻辑是 "枚举所有可能的鸡的数量,计算对应的兔的数量,判断脚数是否符合"。步骤如下:
- 鸡的数量 x 从 0 到 35 循环(因为总数 35)
- 兔的数量 y=35-x
- 计算总脚数:2x + 4y
- 如果总脚数等于 94,输出 x 和 y,结束循环;否则继续枚举
用 Z80 汇编实现这个算法,就是用 B 寄存器存 x(从 0 开始),C 寄存器存 y=35-B,然后计算 2B +4C,和 94 比较,相等则输出结果。这个算法的优点是逻辑简单,容易实现;缺点是效率低,最坏情况要循环 36 次(x 从 0 到 35),虽然 36 次对计算机来说不值一提,但能体现 "优化的思路"。
2. 优化算法
核心是利用数学规律减少枚举次数。我们先推导数学公式:
- 已知 x + y = 35,2x + 4y = 94
- 把第一个式子变形为 x=35-y,代入第二个式子:2*(35-y) +4y=94 → 70-2y+4y=94 → 2y=24 → y=12
- 则 x=35-12=23
优化后的算法步骤:
- 直接计算兔的数量 y=(总脚数 - 2 * 总数)/2
- 计算鸡的数量 x = 总数 - y
- 验证 x 和 y 是否为非负整数(避免输入错误数据),是则输出结果
这个优化算法只需要几步算术运算,不需要循环,直接得到结果,效率比暴力求解高得多。我年轻时用汇编实现这个优化算法,代码行数减少了一半,执行时间几乎可以忽略不计。这个案例告诉我们:算法优化的核心不是 "让代码更短",而是 "利用规律减少计算步骤",这也是教学的重点 ------ 不仅要会实现算法,还要会优化算法。
这里要注意,暴力求解虽然效率低,但不是 "没用"------ 当问题没有明显数学规律,或者规律难以推导时,暴力求解是最稳妥的方法;而优化算法则需要吃透问题的数学本质,适合有明确规律的问题。新手学习时,先实现暴力求解,再思考优化思路,能更好地理解算法的本质和优化的意义。
四、最后小结
现在想想,从早期用汇编实现简单的辗转相除法,到后来用高级语言设计复杂的优化算法,深刻体会到:算法是程序的灵魂,好的程序离不开好的算法。学习算法,不是死记硬背步骤,而是理解 "如何用有限、机械的步骤解决问题",以及 "如何利用规律和技巧让步骤更高效"。
从教学角度,核心要掌握两点:一是典型算法的逻辑实现,比如辗转相除法的余数循环、筛法的批量筛除、线性搜索的哨兵优化,这些是算法学习的基础;二是算法设计和优化的思路,比如利用计算机速度、吃透数字规律、先纸上设计,以及从暴力求解到优化的思维过程。
如今计算机的运算速度越来越快,有人说 "不用在意算法效率了",但我始终不这么认为。哪怕是现在的大型系统,算法的优劣依然直接影响系统性能 ------ 比如搜索引擎的排序算法、大数据的处理算法,差一点的算法可能让系统响应慢几秒,甚至几分钟。就像我年轻时修收音机,好的工序能快速找到故障,好的算法也能让计算机快速解决问题。这也是我想留给年轻程序员的话:代码的语法会过时,但算法的逻辑永远不会,掌握了算法,就掌握了程序设计的核心能力。