前 21 课,我们写的都是"规则明确"的算法:给你输入,程序按我们写死的步骤,算出输出。但现实中有大量问题没法写死规则:房子面积多大,能卖多少钱?一只 28×28 的像素图,是"猫"还是"狗"?这段文字,情感是正面还是负面?这些问题的共性:输入和输出之间有关系,但我们写不出"公式"。机器学习的核心想法来了——与其人工写规则,不如让算法从数据里"学"出这个关系。这一课,我们学机器学习最基础的两块 …

上一课模拟退火是单解型:一个解自己爬山,靠"温度"允许偶尔退一步。这一课换一条完全不同的思路——种群型:不放一个解,放一群解,让它们像生物一样选择、交叉、变异,一代一代演化,逼近最优。这就是遗传算法(Genetic Algorithm, GA)。它的灵感来自达尔文的自然选择:一个种群里的个体有好有坏,环境压力会淘汰差的、留下好的;好个体之间"交配"产生后代,后代还可能发生微小"变异"。反复很多 …

上一课我们搭了元启发算法的总纲,留下一个问题:贪心算法永远只接受"更好的解",于是会卡在局部最优。这一课的主角——模拟退火(Simulated Annealing, SA),就是解决这个问题的第一种经典方案。它的名字来自冶金退火:把金属加热到高温,再慢慢冷却,原子会逐渐排列成能量最低的稳定结构。如果冷却太快,会卡在亚稳态(对应算法的局部最优);慢慢冷却,才有机会到达全局最优。一、先看清敌人:局 …

前面 18 课,我们写的算法有一个共同点:确定性(deterministic)。同样的输入,走同样的步骤,永远得到同样的输出——排序、查找、Dijkstra、DP、回溯,都是这样。但现实里有一大类问题,确定性算法要么慢到算不出来,要么根本写不出精确步骤:圆周率 π 的精确值,怎么写个"确定性步骤"算出来?一个复杂积分,没有初等原函数,怎么求?一个 NP 难题(比如旅行商问题),穷举要算到宇宙毁 …

上一课我们讲了调度——"谁先跑"的问题。但真正的麻烦,是从"真的同时跑起来"这一刻才开始的。当 100 个线程同时去加同一个变量,结果会怎样?这一课,我们要亲手制造一个"并发翻车现场",看看计算机世界里最阴险的一类 bug——竞态条件。从一个"鬼故事"说起想象你有一张银行卡,余额 100 元。某天,你的两笔退款同时到账,各退 50 元。系统应该把你的余额变成 200 元,对吧?但如果程序写错了 …

上一课我们默认"只有一个 CPU 核"。但现实是:你的手机有 8 核,服务器有 64 核;而有些任务——比如无人机飞控、汽车刹车——晚一毫秒都可能出人命。这一课,我们进入调度的两个进阶战场:实时调度 和 多核调度。从"晚 10ms 会怎样"说起想象你在开一辆自动驾驶的车。前方突然出现行人,刹车系统必须在 10ms 内响应,晚一点点就撞上了。这时候,"平均表现不错"够用吗?完全不够。 哪怕 99 …

有些问题,我们找不到"一步到位"的巧妙解法,只能把所有可能都试一遍。比如:8 个皇后怎么摆在棋盘上互不攻击?一个数独怎么填?面对这类问题,回溯(backtracking) 就是那把"系统地穷举"的工具。它不是瞎试,而是一条路走到黑,发现走不通就立刻回头换一条,并且用"剪枝"提前砍掉注定失败的分支。这一课,我们掌握回溯的套路,理解它和 DFS(第 14 课)、DP(第 17 课)的关系。一、回溯 …

先看一个经典问题:爬楼梯。每次可以走 1 级或 2 级,问爬到第 n 级有多少种走法?一个直觉的解法是递归(第 9 课学过):def climb(n): if n <= 2: return n return climb(n-1) + climb(n-2) print(climb(5)) # 8看着很优雅,但它有个致命问题:大量重复计算。算 climb …

导航软件怎么算出从你家到机场的最快路线?地图上成千上万条路,它不可能每条都试一遍。答案藏在两个算法里:Dijkstra 算法(算最短路径)和它背后的思想——贪心(greedy)。这一课,我们认识"贪心"这种最直白的算法策略,并用它推导出 Dijkstra 最短路径算法——这是导航、网络路由、游戏 AI 寻路的底层机制。一、贪心是什么:每一步都挑眼前最好的贪心策略:在每一步,都做出当前看起来最优 …

前面几课,我们处理数据的方式大多是"一次性算完":读进去,算一遍,吐结果。但现实里有一大类问题,是边读边变、边走边看的——你处理到第 5 个字符时的行为,取决于前 4 个字符是什么。比如:你要判断一个字符串里有没有连续的 "ab"。看到 'a' 时你心里想"下一个是 'b' 吗?",看到 'b' 时想"上一个是不是 …