“Packrat解析为固定PEG的每个规则、每个输入位置保存一次识别结果,用记忆化避免回溯时重新计算相同子问题。”
形式陈述
记忆化把递归函数对参数状态
直觉
递归树中许多分支会反复询问同一个子问题;记忆化把已经求出的输入—输出对缓存起来,将递归树折叠成状态 DAG,使同一状态再次出现时直接复用、每个节点只真正求值一次。它保留自顶向下、按需展开的控制流,只计算从初始问题可达的状态;代价是递归开销、哈希或数组查表和缓存空间。正确性依赖函数在同一状态上结果稳定,若状态遗漏了影响结果的环境,缓存会复用错误答案。
例子与边界
令
求
状态键必须包含所有影响答案的信息。区间问题只缓存左端点而遗漏右端点,会把不同区间合并;网格路径只用
无环本身还不足以保证终止:状态
有环依赖需要另行处理。例如
缓存淘汰对纯函数可以保持结果正确,却会让被删状态再次计算,因此不能继续无条件宣称“每个状态只计算一次”。在状态很多或结果很大时,应同时比较缓存空间与重算成本。
推论与应用
解析中的PEG有序选择先规定唯一识别结果,Packrat再以“规则、输入位置”为键,同时缓存成功与失败。固定良构文法、结果不依赖外部可变状态且每格工作有界时,才能由线性表项数推出线性时间;需要保留CFG多种语法树时,不能把所有结果硬压成一个成功终点。记忆化也用于图搜索、游戏求值和最优化递推,关键仍是正确界定子问题及其全部结果。
它是 动态规划 的自顶向下实现,函数 的输入构成状态键。与表格法相比,记忆化适合稀疏可达状态和复杂转移;解析、博弈搜索、递归下降与 DAG 计算都可通过缓存把重叠子问题降为一次求值。
在上述有限无环模型中,缓存不淘汰时,记忆化保证每个实际到达状态计算一次;若状态空间本身为全部子集,便得到子集 DP的指数上界,而非自动多项式。树宽 DP在树分解 bag 上缓存边界赋值,复杂度指数集中在宽度。自顶向下是否访问更少状态取决于实例可达性,最坏仍需与完整状态 DAG 比较,不能把“有缓存”当复杂度证明。
参考资料
-
Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein, Introduction to Algorithms, 4th ed., MIT Press, 2022,Ch. 14 “Dynamic Programming”。
-
Jon Kleinberg and Éva Tardos, Algorithm Design, Pearson, 2005,Ch. 6 “Dynamic Programming”。
-
Erik Demaine and Srini Devadas, MIT 6.006 Introduction to Algorithms, Fall 2011,Lecture 19: Dynamic Programming I — Fibonacci, Shortest Paths。