本文 LLM 使用成分声明:LLM 使用场景在本文主要在数学模型的推演验证和严谨化,现有论文的查询和解析。本篇使用的加权有向 B-超图相关结论验证过程主要使用 claude-opus-5。本文除最后大章的数学论证(AI 编写,人工审核)以外的全部文字叙述,包括数学模型的重构思路,由人工编写,无生成。
上一篇的模型远远不够完善,并且 gpt-5.6-sol 撰写的文章也并没有讲清楚我的思路和心路历程。因而有了改进之后的建模,以及这篇文章。
在 《逃离应试教育,重新学会怎么思考》那篇文章中我提到,学习一个知识体系的过程,本质上是在寻找它的“最小生成元 + 生成规则”,不断压缩信息的过程。
在我自己的学习经历和观察体会的现象当中,我反复认识到,学习路线才是学习效率的决定性因素。不同学习路线带来的学习成本差距是数量级上的差距,远远大于学生个人理解能力、教师面对同一教纲的教学能力、或其他的什么因素。关于这点,我个人的经历可以参考上篇文章的开头。
经历这些事情之后,我经常沉迷于思考这样的一个问题:
怎样才能做到把优质的学习路线的生产和消费转化为社会化的规模?
换句话说,怎样把优质的学习路线从小部分高知群体偶尔个人手工产出,转变为使用机器批量的产出?怎样把定制化的优质学习路线从师徒制的私塾里解放到全社会的学习者触手可及的地方?
既然学习路线才是学习效率的决定性因素,于是我又不得不思考:为什么现代的教育体系中,一般使用的学习路线如此低效呢?在学习目标确定的情况下,如何以更低的成本、更快地获得目标知识呢?
学习效率的矛盾
在复杂事物的发展过程中,存在许多矛盾,其中必有一种是主要的矛盾。
在“学习路线是学习效率的决定性因素”这一前提下,那么提高学习效率的关键就在于学习路线的优化了。
如果实际思考过怎样优化自己的学习路线,实际上会很容易发现下面这些现象。
课程编排不因材施教
学生能接触到的学习路径,通常由学校的课程编排、教材章节或授课老师等外部因素决定。但每个人知识的掌握程度不同,适合的学习路径各不相同,学习目标也不相同,这导致他们实际能接触到的路径往往不符合自己当前的状态的最优解。
同时,为了了解某些具体的现实实用概念的工科学生,却不得不跟着教授编排的教纲,按照理科的标准学完课程参与考试,被迫理解非常多必要之外的知识,就像学习打螺丝,大多数时候并不需要理解螺丝的历史和生产流程。
祖宗之法不可不变
学术界的总体知识在不断进步,新理论的引入能够极大简化知识体系。比如算子理论和希尔伯特空间极大简化了高校工科数学的推导路径,有潜力大幅缩短各类科目的学习路径。
然而学校的教材和教纲往往是祖宗之法,不可变动。这导致实际的教学路径往往明显落后于潜在的最佳路径。
从义务教育到高中到大学,相信读者一定亲身体会过学习了新知识新概念之后,这些新内容对于先前的考试的降维打击。如果在既有上下文一定的情况下,理解相同的新信息需要的成本视作理解能力的话,上面说的这种升年级之后的降维打击并不能体现任何学生理解能力的进步,反而事实上证明了学习路线设计的不合理。
少学反而更慢
有时候,少学点知识反而学的更慢。在引入更多知识的情况下,有时反而大幅提高学习效率。看似反直觉,但例子其实相当多,也相当显然。关于这一点我觉得非常值得单独列出来举几个例子说一下,因为它带来的问题可能比前面两个会更大:
一种情况:假设学习目标是 $A, B, C$ ,传统课程可能只会安排 $A \to B \to C$ 。但实际上存在一个目标外概念 $X$,可以 $A \to X \to B \to C$,乍看多学了一个东西,似乎更慢。但如果:$$w(A, B) = 10, \qquad w(A, X) = 2, \qquad w(X, B) = 1$$直接学 $B$,成本是 10,先 $X$ 后 $B$ ,学习成本是 $2 + 1 = 3$ 。于是 $A \to X \to B \to C$ 反而是更高效的路径:$$w(A \to X) + w(X \to B) < w(A \to B)$$ 然而,继承优秀传统八股文化的考试、面向这样的考试而设立的课程、种种社会压力,无疑会阻止学生学习课纲外的内容,老师们给出的原因无非是高考或是什么的考试里,写这个洛必达不给分云云。有些极端的地方甚至早已形成了嘲讽自行学习微积分等课纲外的高校内容的中学生的歪风邪气,称这些求知的学生为做题区嘉豪,这样的事情在某些高考压力大的省份发生的尤为频繁,有些读者相信会感同身受。至于为什么本该在传说中自我调节的市场经济社会却产生了这样的考试和这样的课程,我们就不深入思考了。
另一种情况:大学的课程往往反复教了同一个东西很多遍,但每一门课都把它伪装成一个全新的技巧。假设学习目标是 $A, B, C$ ,大多中国的学校往往会喜欢这样教:$$\varnothing \to X_1 \to A, \qquad \varnothing \to X_2 \to B, \qquad \varnothing \to X_3 \to C$$ 其中 $w( \varnothing \to X_n )$ 非常大,不同的 $X_n$ 却使用结构类似的推演流程。往往老师这时就喜欢说:“这就是数学之美,冥冥之中存在着联系。” 台下学生不明觉厉,深以为然。但是,如果首先进一步发现 $X$ 本身就是一个更高层次的共同结构,此时由 $X$ 可以衍生的概念的学习效率会发生质变。进而可以以极低的成本:$$X \to {X_1, X_2, X_3}$$ 那么再找到 $A, B, C$ 等特例的成本也极低。因为 $X$ 作为一次抽象产生的知识,可以作为多个后续节点的共同前置条件:$$w(\lbrace X, X_1\rbrace \to A) < w(X_1 \to A)$$ 以我的学校的课程编排举例,具体编排可能忘了,可能大概是:$$高数→线性代数→概率论→离散数学→信号与系统$$ 到了信号与系统,学生会突然被要求学习傅立叶级数、傅立叶变换、拉普拉斯变换、Z 变换、卷积、频域、系统函数。可能自动化学院在自动控制里面,又要学一遍拉普拉斯,极点零点,频率响应,到了数字信号处理,把 Z 变换、DTFT、DFT、FFT 又学了一遍。这很容易有一种强烈的错觉,这些都是不同课程里的不同形式,我得分别记住它们的公式和推导和性质。但如果你稍微越过传统工科教纲一步,先引入一个更高层次的概念,只要有非常入门级的算子理论,理解函数空间上的内积与正交展开,把求导变成作用在函数空间上的算子 $D$ ,立刻可以复用线性代数的知识,就像特征向量一样,因为 $De^{i\omega t} = ie^{i\omega t}$ ,自然就知道它是微分算子的特征函数,所以傅立叶分析的核心思想自然而然就成为了:找到一组在某个算子作用下行为特别简单的基函数。换成 $e^{st}$ ,变成复平面,就成了拉普拉斯变换,微分算子换成移位算子,就变成了 Z 变换。如果进一步引入算子谱定理,立刻就知道,微分变换成乘法是来自微分算子的谱表示,卷积变成乘法来自平移算子及其谱表示,各种逆变换来自谱表示的可逆重构,频域求解微分方程本质上是把算子变换到更容易求逆的表示中。例子太多,再说就扯远了,但足以证明,有些教学路线为了避免让本科生提前接触函数空间、算子和谱理论,看起来少学了一些抽象数学,却不得不让学生在后续课程中反复证明同一类性质、反复记忆不同变换的公式。
上面这种类型的类似错误教学方式还有很多,它们已经在事实上荼毒学生的思维。因为它们扼杀了学生的求知欲,转而又进一步封锁了从感性认识上升到理性认识的通道。
“冥冥之中万物有联系”,终究是停留在冥冥之中的感性认识,就像是中医。不去上升到理性认识,反而是不断强化形而上学的规律,最终沉沦于题海之中,把学生当成大模型,把题目当成语料去训练,拟合考试题目。最终并不会真正习得背后的规律,结局反而是过拟合,丢失理解规律背后本质的能力。相当多的学生仍然蒙在鼓里,把重复反复推演的痛苦等同为学习的痛苦,在以后的自学道路上也这样做,百害而无一利。
真正的主要矛盾
上面这三条现象可以被分别概括为这三条矛盾:
- 外部决定的路径与个体适配路径的矛盾
- 固定教学大纲与学术界知识演进的矛盾
- 学习内容最小化与学习成本最小化之间的矛盾
我们不难发现它们总是存在的共同点。现代教育系统能够让学生接触的路径是预先划定范围的、静态的、标准化的路径,而学习本身却是不限范围的、动态的、个人化的过程。因此我们总结:
学习效率的主要矛盾,是既定学习路线自身的局部静态与最优学习路线的全局动态之间的矛盾。
事物矛盾的法则,是对立统一的法则。既定学习路线和最优学习路线,相互对立,又相互统一;互为依赖,又相互制约。
既定学习路线要沿着当前路线走,找到最优路线要不断重新选择路线,这体现了二者的对立。
但如果完全没有既定路线,每学一个概念都重新搜索整个知识体系;反过来,如果不去寻找全局动态最优,你永远执行第一次制定的路线,那么你又无法利用时刻产出的新知识。
因此,“如何提高学习效率”这一核心问题,现在变成了:
如何在全局知识和已有知识不断变化的过程中,持续找到通往目标知识的最优路径。
2026.8.25 编辑:
思路差不多说完了,打个广告,对这个话题感兴趣的可以加群一块讨论:

学习路线的建模历程
最开始,我还没有意识到这是个动态优化问题。三个月前我最开始其实只是想做一个优质教程共享平台,让任何人都可以写教程,把优质的教程放在平台上,辅以 AI 帮助教学和检验理解,引入交互式的可视化的教学元素。希望这样一来,优质的教程自然会被用户选择,进而被热门推送。
首先是尝试我一直惯用的套路,直接去列几个对象,再给对象列字段等等,准备拿它做数据库表。大概分了 Course, Chapter, Concept 等层级,尝试让 AI 判断用户是否真正理解概念,其实还是受现有的教学逻辑限制,是一套形而上学的思路。
这时我发现,我的教程仍然是一个线性的固定的教程,问题随之暴露出来:概念学习并没有唯一顺序。学习信号与系统时,欧拉公式只需要在第一次使用前掌握,并不天然属于某一章的固定位置。因此教程不一定非得是线性的,但模型限制了这一点。
于是我把教程变成了图,章节是点。但不同章节各种概念有相互依赖,有包含,有相关等关系,只好把章节之间的关系分成多种类别。
我被逼到开始用图表达知识,却仍然被限制在传统的科目、教材、章节、小节的框架里。最后我才意识到问题。既然核心问题是找到最优路径,事实上为什么我不直接对知识本身进行建模?
能否把学习路径变成一个可以被建模、计算和优化的问题?形象一点的说,能不能把知识体系变成地图,在这张地图上做导航软件?
我们不得不需要一个可靠的参照来指导我们软件的代码实现,于是我重新开始尝试建模。
任何的要学习的知识都是一个“概念”,我要学数学,数学是个概念,线性代数是一个概念,行列式又是一个概念,同一层级的概念可以相互推演,概念又可以被拆分为更小的概念,可以组合成更大的概念。似乎所有要学习的内容都是一个概念。抱着这样的想法我开始尝试把之前的概念画成不区分教程、章节等层级的,可以嵌套的思维导图,一切都只是概念。
在草稿纸上画了半天,我发现这些逻辑用自然语言或是图形语言是根本描述不清楚的:它容易漏掉一些情况,会在一定程度上导致模糊的行为。我指的“漏掉情况”,并不只是漏掉边界条件这么简单,而是指那些会直接影响整个模型是否可以稳定写成代码,找到路径的算法是否可实现的关键情况。我不希望在付出代码实现成本之后,回头才发现这些问题。
所以为了必须做到这种精度的建模需求,我选择引入数学工具来描述数据模型,这就是我为什么需要用数学。数学从来都不是被用来伪装思路正确的东西,数学是一个帮助人把问题抽象化的工具,帮人把问题真正理清楚。
上一版 AND / OR DAG 的模型问题
上一篇《万物皆概念:如何最大化学习速度?》中,我已经正在着手做一个“万物皆概念”版本的知识图谱数学模型。
我先大概简述一下上一篇所建立的模型经历的过程:我把概念视为点,相邻概念之间的推导视作有向边,把无环作为模型的公理,尝试构建一个概念的 DAG 图(有向无环图)。当时引入无环公理是为了避免教程里出现循环论证,真的有环的话,打算让用户另外起一个 Wiki 图,因为当时认为应当不属于同一份教程。在此基础上,定义如何把某一概念拆分成更细的 DAG 图,定义如何封装 DAG 子图为更大概念。通过给边加权,权用来代表学习成本,进而实现动态规划最优路线。
然后我发现,推导并不是简单的一个概念指向另一个概念,推导可能是由众多概念推出来下一个概念。因此我把每一个概念的入边进行了分组,属于同一个推导流程的分为一个 AND 组,同一个概念的入边 AND 组之间的关系是 OR。
然后我发现我漏掉了一件事,每个概念的推导过程等属性还被我落在边上,而不是 AND 组上。于是在上篇文章结尾,我在 2026.8.19 补了一处修正:问题引入、推导解答和理解材料,不属于单条边,而属于整个 AND 组。
当时以为是一个小修正,写完发现实际上这引导出了 AND / OR DAG 这个模型体系的一些固有问题。AND / OR 似乎是一个描述太过复杂的对象,不便推演。同时,我使用了太多教学和学习领域专有的内容作为名词,并没有把它当成一个真正的数学模型,这会导致并不能专注于模型本身。
但这次数学建模的尝试真真确确已经带来了相当多的好处,也让我坚定了数学建模这条路。建立模型之后,通过套用前人的数学结论,往往可以得到一些之前从来没考虑到的优秀性质,可以立刻察觉到之前从来没考虑到的逻辑疏漏,可以帮我提前排出一些技术实现的风险。
当我愿意把模型当作纯数学对象进行处理时,也才能意识到这样的数学对象事实上不止可以适用于规划学习路线,事实上可以用于规划任何路线,典型的例子就是硬件仿真路线的优化。这在透过现象看本质之前,是不可能实现的。一旦把具体对象抽象成它背后的关系结构,原本只存在于一个领域中的规律,才有可能被迁移到另一个领域。
借助这些探索,我也从数学推演中反应过来一些之前从没考虑到的问题:
- 知识并不是以 DAG 的形式存在的,有些知识恰恰就是可以以环的形式存在,相互推演得到的。例如通过欧拉公式,我们可以从指数函数得到正弦函数,也可以从正弦函数得到指数函数。我想避免的只是同义反复,例如线性和叠加原理。如果一个教程里说,线性就是满足叠加原理,叠加原理是线性系统的性质,那等于是什么都没说,相当于引入了一条公理而不是定理。
- 概念不是可以随意拆分的,也不是可以随意封装的。它远远不只是把其中一段 DAG 在纸上画一个圈标出来这么简单。这导致之前想象的概念的拆分和封装的预估难度骤然变大。但也不是说不可实现,因为概念的封装和拆分显然是现实中存在,并且高度有价值的。
我反思。当时试图把它做成一个 DAG,引入无环这一模型中的公理,是为了避免教程中出现循环论证。可是我的初衷却是对知识进行建模。教程只会是知识的一个子图,我却让教程的性质变成了知识的公理。我只需要教程是无环的即可,不需要让知识也是无环的。我又一次被做教程的思维限制住了思路。
之后我尝试重构了这个模型。上一版模型最严重的几个问题反而不是上篇文章末尾的几个问题了,实际上最大的问题总结如下:
- 没有专注于数学本身。
- 为了弥补多条件的知识推导而引入了 AND / OR ,不论从代码实现还是数学论证时的观感上都像是补丁的东西,又把推导成本,这个权重给粘在了 AND 组上面。
- 把知识图谱限制为无环,限制了表达的上限,不同教程无法在同一张知识图谱存在,违背时刻计算新学习路线的初衷。
因此,如何改进这个模型也是明确的:
- 专注于数学对象本身。
- 把 AND 组单独作为对象,而放弃边。
- 验证,如果不限制无环,模型是否还能成立。
加权有向 B-超图
上面三条改进目标,最后收敛成了一个数学对象:加权有向 B-超图。
它的全部内容是:一堆点,和一堆「多个前提共同推出一个结论」的带权连接。没有概念,没有教程,没有 AND 组,没有 OR 组,也不要求无环。
定义就这么点,但立完之后能推出来的东西超出我的预期。为了避免读者在后面七节里迷路,先把结论摆出来:
- AND 组和 OR 组可以被完全消掉。 不是绕过去,是能构造双射证明它们不携带任何独立信息。
- 权只能挂在超边上,边级权在数学上无解。 这是上一篇结尾那处修正的完整版本。
- 无环公理可以删掉,而且删掉后模型比原来更宽松。 欧拉公式那种好环得以保留,线性与叠加原理那种同义反复自动失效。
- 可达性判定是线性时间的。 因为它和 Horn 子句的最小模型是同一件事。
- 我真正想要的那个成本口径是 NP-hard 的,而且不可近似。 这是整个模型里最坏的消息。
- 但它被夹在两个多项式可算的界之间,而且上界自带一条真实可走的路径。 于是任何时刻都有可推荐的路线,外加一个「最多差多少」的证明上界。实测还要好:随机图上区间有 97~99% 直接闭合,连搜索都不用启动。
- 区间宽度本身又是一个知识图质量的诊断指标。 代价是它同时也是难度指标:图越接近我们想要的样子,就越靠近难解那一端。
- 「理解倍率」不能定义为路线计数。 那是计数困难问题,上一篇里的那个设想可以判死刑了。
- 概念封装只在两个严格条件下保真,而上一版凭直觉立的「禁止递归交集」,恰好就是其中之一。
下面七节先把这个对象定义清楚,再逐条给出上面这些结论的证明或反例,并说明它们解决了上一版的哪些问题、又留下了哪些新问题。
下面每一条结论我都要求自己要么给出证明、要么给出反例,能写成代码对拍的一律对拍。全部代码在 derivon-research/paper,无第三方依赖。根目录的 derivon_core.py 是参考实现(线性时间闭包、两个多项式界、集合成本的分支定界),verification/ 下是各条结论的机器验证。后文每处实测数据都会链到对应的那段代码。
阅读前的数学补充
这一节是给数学基础不好的读者准备的。全文真正用到的工具只有四类:集合、函数、逻辑连接词、求最小值。另有三个稍专门的概念:最小不动点、超图、计算复杂度。熟悉的读者可以直接跳到下一节。
符号速查。
| 符号 | 读作 | 意思 |
|---|---|---|
| $p \in P$ | $p$ 属于 $P$ | $p$ 是集合 $P$ 里的一个元素 |
| $T \subseteq P$ | $T$ 包含于 $P$ | $T$ 的每个元素都在 $P$ 里 |
| $U \cup V$ / $U \cap V$ | 并 / 交 | 合起来 / 公共部分 |
| $\varnothing$ | 空集 | 没有任何元素的集合 |
| $\lvert U \rvert$ | $U$ 的势 | 集合里元素的个数 |
| $2^{P}$ | $P$ 的幂集 | $P$ 的所有子集组成的集合;若 $\lvert P \rvert = n$ 则共 $2^{n}$ 个 |
| $f : U \to V$ | $f$ 从 $U$ 映到 $V$ | 一个函数,输入取自 $U$,输出落在 $V$ |
| $\wedge$ / $\vee$ | 且 / 或 | 逻辑连接词 |
| $\Rightarrow$ / $\iff$ | 推出 / 当且仅当 | 单向蕴含 / 双向等价 |
| $C^{*}$ | $C$ 星 | 上标星号表示「最优值」 |
| $O(n)$ | 大 O | 耗时随规模增长的量级 |
集合语言。 三个集合贯穿全文:$P$ 是全体点,$S$ 是学习者已经掌握的点,$K$ 是推导过程中的当前状态。学习的过程就是 $K$ 从 $S$ 开始不断变大。「$T$ 里所有点都已掌握」这句话,写成集合语言就是 $T \subseteq K$——不需要逐个检查,一个符号就说完了。
双射。 要证明「换一种写法没有丢信息」,标准做法是构造一个来回都能走通、且走一圈回到原处的对应,叫双射。能构造出来,就说明两种写法携带完全相同的信息,删掉一边不损失任何东西。后面「AND / OR 组可以被完全消掉」就是这么证的。
两种范式。 同一个逻辑条件有两种标准写法。合取范式是「若干个『或』用『且』连起来」,例如 $(I_1 \vee I_2) \wedge (I_3 \vee I_4)$;析取范式是把它展开成「若干个『且』用『或』连起来」,即 $(I_1 \wedge I_3) \vee (I_1 \wedge I_4) \vee (I_2 \wedge I_3) \vee (I_2 \wedge I_4)$。两者逻辑等价,但长度可以差很多:左边 4 个条件,右边 4 项;若有 $k$ 组这样的「或」,左边是 $2k$ 个条件,右边却有 $2^{k}$ 项。这个爆炸后面会在概念封装那里原样出现。
最小不动点。 「不动点」指对某个操作而言输入等于输出的东西。本文的操作是「扫一遍所有连接,把前提已经齐了的那些的结论加进来」。算法上就是最朴素的做法:从 $S$ 出发反复扫描,直到某一轮没有任何新东西可加。因为点的总数有限、集合只增不减,这个过程一定会停——这就是「一定存在且唯一」的工科版理由,Knaster–Tarski 定理1只是把它严格化。
图与超图。 有向图是点加带箭头的边。路径是一串首尾相接的点,走成一条线,既不分叉也不汇合。超边把「一条边只能一个起点」的限制放开:$(\lbrace A,B \rbrace, D)$ 表示 $A$ 和 $B$ 都有了才能得到 $D$,画出来是两条线汇进一个箭头。B-超图指每条超边只有一个终点,「尾」指前提集合,「头」指结论。
复杂度。 $O(n)$ 表示耗时大致随规模线性增长。NP-hard 的实践含义是:不要指望写出一个在所有输入上都快的精确算法。归约记作 $\Pi_1 \le_{p} \Pi_2$,含义是「如果我能快速解 $\Pi_2$,就能拿它快速解 $\Pi_1$」,所以 $\Pi_2$ 不会比 $\Pi_1$ 容易。不可近似比 NP-hard 更强一层:不但精确解算不出来,连「保证结果不超过最优解若干倍」的打折算法也不存在。
分层与核心对象
上一版最大的毛病是「没有专注于数学本身」。要治它,得先有一条硬判据,否则「学习」相关的词还会不断渗回来。
我定的判据只有一条:一个字段只有在被核心算法读取时,才能进入核心层。 核心算法只有三个——可达性判定、最低成本求解、可执行序生成。按这条筛,上一版的 definition、question、answer、materials 全部出局,它们从不被这三个算法读取;权留下,因为求解器读它。
于是分成三层,下层不知道上层的存在:
flowchart TB
APP["应用层:学习路线规划 · 硬件仿真迭代 · Agent 任务规划"]
MOD["模块层:概念的封装与展开(可选,可以整体不要)"]
CORE["核心层:点 · 超边 · 图 · 成本"]
APP -->|依赖| MOD
MOD -->|依赖| CORE
APP -.->|"标定权重"| CORE
核心层一共七个名字,其中只有前三个需要持久化:
| 名称 | 记号 | 含义 |
|---|---|---|
| Point | $p \in P$ | 基本对象。核心层不假设它有内部结构,也不承诺它代表什么 |
| Hyperedge | $h = (T, y, w)$ | 一个推导步骤。$T \subseteq P$ 是尾(前提集),$y \in P$ 是头(结论),$w \ge 0$ 是完成这一步的成本 |
| Graph | $G = (P, H)$ | 加权有向 B-超图 |
| State | $K \subseteq P$ | 当前已获得的点集 |
| Derivation | $R \subseteq H$ | 见证目标可从起点得出的一整组超边 |
| Query | $(S, t)$ | 起点集与目标点 |
| Cost | $C(R)$ | Derivation 的成本 |
这里有个容易混的地方要先说清:Hyperedge 是一个步骤,Derivation 是一整组步骤。 相应地 $\lvert T(h) \rvert$ 说的是「某一步需要几个前提」,$\lvert R \rvert$ 说的是「这次推导一共有几步」。另外,$T$ 取自 tail(尾),不是 target;目标一律用小写 $t$。
一步推导的规则简单到有点无聊:
$$h = (T, y, w) \ \text{可执行于} \ K_i \iff T \subseteq K_i, \qquad K_{i+1} = K_i \cup \lbrace y \rbrace$$
尾集为空是合法的。$T = \varnothing$ 时 $T \subseteq K_i$ 恒成立,所以 $(\varnothing, y, w)$ 在任何状态下都可执行,语义上是「无条件起点」:不问学习者已经会什么,付出 $w$ 就直接获得 $y$。这和「点落在 $S$ 里」不是一回事——$S$ 描述的是学习者(因人而异,不计成本),空尾超边描述的是图本身(对所有人一样,要付成本)。一张没有任何空尾超边的知识图,零基础学习者什么都学不了,因为没有任何一步能起步。
走一遍就清楚了。设起点 $S = \lbrace A \rbrace$,目标 $Z$:
| |
$K$ 的全部作用就是回答「现在够不够」。最后那一步 $\lbrace D,E \rbrace \to Z$ 需要两个前提,两个都有才能走——这就是上一版要靠 AND 组才能表达的东西,现在只是尾集大小的问题。
四条化简
有了上面的定义,上一版那些让我别扭的东西可以一条条被证明为多余。
一、AND 组和 OR 组都不需要存在。
上一版里,同一概念的入边被分组,组内是 AND、组间是 OR,还得维护「一条边可以属于多套方案」这种多对多关系。整套结构在超边表示下是空的。
$\lvert T(h) \rvert > 1$ 就是 AND,尾集本身就是合取;两条头相同的超边并存就是 OR,它们彼此独立,不需要任何分组结构管理。严格地说,上一版的方案集合 $\mathcal{P}(v)$ 与「头为 $v$ 的超边集合」之间存在双射:一条边由 $(\text{source}, \text{target})$ 唯一标识,所以方案 $P$ 与其源点集一一对应,取 $\Phi(P) = (\lbrace \text{source}(e) : e \in P \rbrace,\ v)$,逆映射把超边展开回逐条边,两者互逆。既然是双射,删掉一边不损失任何信息。
顺带地,上一版那个「投影边」对象也一起没了——它只是超边在有向图上的展开视图,随时可以重算。
二、权只能定义在超边上,边级权在数学上无解。
这条正是上一篇结尾那处修正的完整版本。我当时的直觉是权不可能挂在边上,但说不出为什么。反例比我想的干脆:
$$(\lbrace A,B \rbrace, D, 1), \qquad (\lbrace A \rbrace, D, 5), \qquad (\lbrace B \rbrace, D, 5)$$
它描述的情形很常见——$A$ 和 $B$ 各自单用都很贵,合起来却有极强的组合效应。若权可以拆到边上,则必须同时满足
$$w_e(A \to D) + w_e(B \to D) = 1, \qquad w_e(A \to D) = 5, \qquad w_e(B \to D) = 5$$
也就是 $10 = 1$。这个方程组的最小二乘残差是 $5.196 > 0$,确认无解(test_no_edge_weights)。
更根本的是,即使不存在竞争方案,把一个整体成本拆成各前提的份额也没有典范选择——任何和等于总数的拆分都同样合法;而一条被两套方案共享的边会被迫同时取两个值。结论是权和载荷同源:二者都是定义在整个推导步骤上的函数。既然同源,「方案」和「边」就没有理由并存为两个对象。
三、无环公理可以直接删掉。
这正是我前面反思的那条:我只需要教程无环,不需要让知识无环。数学上给出的答案比我预期的更干脆——这条公理根本不必存在。
闭包是最小不动点,由 Knaster–Tarski 定理1保证存在且唯一,与图里有没有环无关。而循环支撑的点永远不会进入闭包:假设 $A$ 要靠 $B$ 推出、$B$ 又要靠 $A$ 推出,且两者都没有别的来源,那么第一轮扫描时 $A$ 的前提 $B$ 不在集合里,$B$ 的前提 $A$ 也不在,两条超边都不触发;此后集合再没变过,所以永远不会触发。没有源头的循环不会自己启动。同一张图上换个有根的起点,那两个点就都推得出来了(test_cycle_safety)。
四、载荷退化为不透明字段。
definition、question、answer、materials 都不被三个核心算法读取,统一为一个核心层不解释的字段。核心层唯一需要的是:成本 $w$ 是这些载荷的某个函数——这个函数由应用层实现和标定,核心层只接收结果。
四条合起来,对象数量的变化是:
| 维度 | 上一版 | 现在 |
|---|---|---|
| 持久化对象类型 | Concept / DerivationPlan / Derivation,另有 AND / OR 分组 | Point / Hyperedge |
| AND / OR | 需要专门的分组结构 | 尾集大小与同头并存,不是对象 |
| 模型公理 | 无环、禁止递归交集 | 无(下面会说交集约束去哪了) |
| 权的载体 | AND 组 | 超边(同一对象) |
| 顺序机制 | DAG 拓扑序 | 可执行序,不依赖无环 |
可达性:好环、坏环与静默失败
把超边 $(T, y, w)$ 读作「$\bigwedge_{p \in T} p \Rightarrow y$」,起点集读作事实集,那么闭包恰好就是这个 Horn 程序的唯一最小模型。这不是类比,是同一件事——由此直接继承一个强结论:可达性判定可在线性时间内完成2,具体是 $O(\sum_h (\lvert T(h) \rvert + 1))$。为每条超边维护一个「未满足前提计数」,某点首次入队时把以它为前提的超边计数减一,减到零即触发。
这条对应关系也解释了经典 AND/OR 图理论3为什么天然适用:AND/OR 图、Horn 子句、有向超图、上下文无关文法的推导,在文献里本来就是同一结构的四种写法4。这一版只是选了其中最紧凑的一种当实现基底。
顺序机制也随之换掉了。上一版用 DAG 的拓扑序给出「合法讲述顺序」,而拓扑序只在无环图上存在。现在改用可执行序:$R$ 的一个排列,使得每一步在它之前那些步骤的结果之上都可执行。闭包的分轮扫描顺手就产出一个,所以任何 Derivation 至少有一个可执行序,且不需要全图无环。
现在回到我前面说的那个反思:知识里有些环是合理的。数学给出的答案比「允许环」更精确——它区分了两种环。
flowchart TB
subgraph GOOD["好环:两条边都是正当推导,互为 OR 路线"]
direction LR
T(["泰勒级数(入口)"]) --> E["e^ix"]
G(["几何三角(入口)"]) --> SC["sin/cos"]
E -->|"欧拉公式:取实部虚部"| SC
SC -->|"欧拉公式:反方向"| E
end
subgraph BAD["坏环:互相定义,整组没有任何外部入口"]
direction LR
L["线性"] -->|"就是满足叠加"| SP["叠加原理"]
SP -->|"是线性的性质"| L
end
带泰勒级数进来的学习者能到 $\sin/\cos$,带几何三角进来的能到 $e^{ix}$,两条路都通,环的存在对谁都没有造成任何阻碍。而「线性 $\rightleftarrows$ 叠加原理」这种同义反复,无论学习者已经掌握了什么,两个点永远推不出来。
所以准确的说法是:这一版拒绝的不是环,而是没有外部入口的环,并且把判断推迟到有具体查询的时候。 上一版的无环公理是全图的结构禁令,会把上面那张图整张拒收,逼作者从 $e^{ix}$ 与 $\sin/\cos$ 之间删掉一条——而删哪条都是在丢弃一条真实的教学路线。
新问题也随之出现:环从语法错误降级成了静默失败。 上一版里作者画出一个环会立刻被拒绝,现在它只表现为「目标不可达」,原因不再显然。所以核心层必须补一个诊断接口,把「声称能推出、但那条推导是循环的」点找出来。
这个诊断比看上去难写。我第一版判据是「假设除它自己以外一切已知,看还能不能推出它」,看着合理,实际上既误报了正常入口,又漏掉了真正的坏环——因为循环里的两个点会互相顶上,把检查骗过去。正确判据是从入度为零的点(图的天然入口)出发算闭包,落在外面的才是悬空点。上面两种环的完整算例、以及错误判据作为反例,都在 cycle_demo.py 里带断言保留着。
成本:一个坏消息和一对界
这一节是整个模型里唯一一处必须做有代价取舍的地方。在给出任何口径之前,先把两条公理摆出来,它们不是推出来的而是被假定的:
- 可加性:一条推导的总成本,由各步成本按某种固定规则聚合而成。
- 非负性:$w \ge 0$。
两条都不是事实。真实认知成本会被疲劳、干扰、遗忘破坏可加性;而「先学 $A$ 让后学 $B$ 变便宜」这类正迁移本应是负成本,非负性却禁止它——只能改用「新增一条以 $A$ 为前提、权更低的替代超边」来间接表达。
我上一篇里写过一句当时觉得理所当然的话:共享的前置推导在同一子图中只计算一次。 这句话是整个模型的核心主张,前面「少学反而更慢」那一节里 $X$ 作为多个后续节点的共同前置,正是靠这条才成立。如果复用要重复付费,高压缩率路线这个概念本身就不成立了。
问题是,这个口径算不出来。看一个具体例子,学习者要学会 $Z$:
| |
三种算法给出三个不同的数:
| 口径 | 怎么算 | 结果 | 对应现实 |
|---|---|---|---|
| 集合成本 | 用到的四步各算一次:$3+1+1+1$ | 6 | $B$ 只学一遍。这是真实的学习成本 |
| 展开成本 | 摊成一棵树,$D$ 和 $E$ 往回走都要经过 $B$:$(3+1)+(3+1)+1$ | 9 | 假装 $B$ 学了两遍,高估 |
| 深度成本 | 最长的一条链 $A \to B \to D \to Z$:$3+1+1$ | 5 | 可并行推进时的最短完成时间,低估 |
我想要的是 6,但能高效算出来的是 9 和 5。
原因在于局部性。展开成本和深度成本都能写成一条「只看直接前提」的递推式:
$$d(y) = \min_h \Big[, w(h) + \sum_{p \in T(h)} d(p) ,\Big], \qquad d(y) = \min_h \Big[, w(h) + \max_{p \in T(h)} d(p) ,\Big]$$
要算 $Z$ 的成本,只需知道 $D$ 和 $E$ 各自的成本,再加最后一步。这种局部性让 Dijkstra 式的贪心成立——正是 Knuth 在 1977 年推广 Dijkstra 算法时刻画的上位函数条件5:聚合结果对每个自变量单调不减,且不小于任何一个自变量。
集合成本没有这个性质。$D$ 那一支到底要不要为 $B$ 付钱,取决于 $E$ 那一支是不是已经买过了。成本不再是各分支成本的函数,而依赖于全局已经选了哪些边,贪心就此失效。
而且不只是「暂时没想到好算法」,它是 NP-hard 的。由集合覆盖归约:起点 $z$、目标 $t$,每个子集 $V_j$ 对应一个点 $x_j$ 与超边 $(\lbrace z \rbrace, x_j, 1)$(选用这个子集要花 1),元素 $q_i \in V_j$ 时有 $(\lbrace x_j \rbrace, q_i, 0)$(免费得到它覆盖的元素),最后一条 $(\lbrace q_1,\dots,q_n \rbrace, t, 0)$(凑齐全部元素才能到 $t$)。要到达 $t$ 就必须得到全部 $q_i$,因而所选 $x_j$ 必须覆盖全集,总成本恰好等于所选子集的个数——于是「用最低成本到达 $t$」和「用最少的子集盖住全集」是同一个问题。而集合覆盖不但 NP-hard,还不可在 $(1-o(1))\ln n$ 因子内近似6,连打折的算法都不存在。这个归约我在 60 组随机实例上对拍过暴力集合覆盖,结果精确相等(test_reduction)。
文献里其实早有这个现象的一般描述:超路径的求解代价随所选度量,可以从线性时间一直变化到 NP-hard7。我只是撞上了这个谱坏的一端。
难受完之后我发现,这三个数之间有关系。设权非负,则
$$d_{\text{depth}}(S,t) \ \le\ C^{*}{\text{set}}(S,t) \ \le\ d{\text{tree}}(S,t)$$
右边:取展开成本最优解所用的超边多重集,它去重后的支撑集是一个合法解,而把重复计的账改成只计一次,总数只会变小或不变。左边:非负权下,最优解上的最长链至多用到互不相同的超边,所以链长不超过全部权之和。当所有超边都只有一个前提时,推导退化成一条链,三者重合于经典最短路径。这对不等式我在 1753 组随机可达查询上用穷举法逐一对拍过,其中 67 组出现严格不等,说明这对界不是平凡重合(test_bracket)。
两头都是多项式可算的,于是求解策略就有了:先算两个端点,若相等则中间那个 NP-hard 的量被夹死,直接返回、根本不用搜索;不相等才启动搜索,用展开成本的解当初始上界、深度成本当可采纳启发;超预算就返回区间并标记未收敛,绝不用展开成本冒充精确值。这套策略实现在 min_set_cost 里,并用穷举当预言机对拍过 1325 组随机查询,结果完全一致(verify_core.py)——分支定界快但复杂,穷举慢但不容易写错,两者对拍是唯一能防止「快但错」的东西。
这里有一点值得单独强调:展开成本不只是个数字,它对应一条真实可走的推导。 所以这不是「算不出最优就没边」,而是一个货真价实的 anytime 算法:任何时刻都有一条可推荐路径,外加一个经过证明的「最多差多少」。对一个实际产品来说,这已经够用了——学习者需要的是一条好路线,不是一个最优性证明。
还有一个我没预料到的副产品:区间宽度 $d_{\text{tree}} - d_{\text{depth}}$ 本身就是一个可观测量,它度量的正是当前知识图里共享结构的强度。如果这个宽度恒等于零,说明图退化成了一堆互不复用的线性教程——这恰好就是前面「少学反而更慢」那一节所批判的教纲形态。一个知识图质量的诊断指标,从一个纯粹的算法困难里掉了出来。
那么这个区间到底有多紧?夹逼定理只保证区间存在,没说它有多宽。而宽度直接决定了上面那套策略到底值不值钱,所以我又写了一个脚本实测(bracket_practice.py)。
第一组:随机超图。 每组几百次可达查询,统计有多少比例需要真的启动搜索:
| 尾集上限 | 样本 | 需搜索占比 | 平均宽度 | 上界高估倍数 |
|---|---|---|---|---|
| 1 | 678 | 0% | 0.00 | 1.00x |
| 2 | 565 | 2% | 0.09 | 1.39x |
| 3 | 467 | 3% | 0.10 | 1.34x |
| 4 | 401 | 1% | 0.06 | 1.33x |
也就是说,97~99% 的查询里廉价上界恰好就是精确解,那个 NP-hard 的量被直接夹死,根本不需要搜索。尾集上限为 1 时宽度恒为零,因为没有汇合就没有共享。
但这组数据不能直接拿来乐观,因为随机图恰恰不具备我们关心的结构。随机采样很少产生「一个点同时是多个分支的前置、而那些分支稍后又汇合」这种形态。
第二组:刻意构造的枢纽结构。 一个高压缩率中间概念 $X$ 被 $k$ 个分支复用,目标需要全部分支——正是前面「少学反而更慢」那一节鼓励的那种形态:
| 分支数 $k$ | 枢纽成本 | $d_{\text{depth}}$ | $C^{*}_{\text{set}}$ | $d_{\text{tree}}$ | 宽度 | 上界高估 |
|---|---|---|---|---|---|---|
| 2 | 2 | 4 | 5 | 7 | 3 | 1.40x |
| 3 | 8 | 10 | 12 | 28 | 18 | 2.33x |
| 5 | 2 | 4 | 8 | 16 | 12 | 2.00x |
| 5 | 8 | 10 | 14 | 46 | 36 | 3.29x |
规律很清楚:分支越多、枢纽越贵,廉价上界越离谱。 而这两个量恰恰就是「这个中间概念抽象得越好」的表现。
于是得到一个反直觉的结论:区间宽度同时是「图质量指标」和「问题难度指标」,两者是同一个量。 图越接近我们想要的样子——高复用、多条 OR 路线——它就越靠近难解那一端。
所以「等规模大了再说」这个安慰实际上是「等图还足够松散、树状」,而产品做成功恰好把图推向反方向。这不是现在要担心的事,但值得记一笔:别指望「图小就没事」,该指望的是「分支定界在这类枢纽结构上跑得动」——后者是可以实测的,不是猜的。
表达力的边界
定义立好之后,可以反过来问:这个对象能表达什么,不能表达什么。
单头够不够。 B-超图规定每条超边只有一个头。若想表达「$T$ 共同推出 $y_1 \dots y_k$,总成本 $w$」,可以引入中间点 $p^{\star}$,写成 $(T, p^{\star}, w)$ 加上若干条零权的 $(\lbrace p^{\star} \rbrace, y_i, 0)$。这个编码在可达性和集合成本下保真,但在展开成本下不保真——后者会把 $p^{\star}$ 按头的个数重复计费(实测:集合成本 4,展开成本 8,见 test_multihead)。所以核心层维持单头,代价是多头场景下上界会变松。
半环推广。 把 $(\min, +)$ 换成别的半环,同一套结构立刻覆盖更多问题8:布尔半环给出纯可达性,计数半环给出推导条数。但广义 Dijkstra 只对上位函数成立,其他情形要退回不动点迭代。这一步我没走——首版把权类型参数化但只实现 $(\min, +)$ 就够了,过早抽象没有收益。
计数是硬的。 上一篇我提过一个「理解倍率」的设想:每完成一条不同的有效路线就增加 100% 的理解度。现在可以判它死刑了——统计极小推导的个数,稍作修改就能归约到统计最小集合覆盖的个数,属计数困难问题,而且极小推导的数量本身可以随图规模指数增长。理解倍率不能定义为路线计数,只能换成可控的替代量,并明确标注为工程近似。
模块层:封装的真实代价
最后是我前面反思里的第二条:概念不是可以随意拆分和封装的。这一节给出它到底难在哪。
先说一个结构上的好消息:封装整体被移出了核心层。 核心层的三个算法都不读取包含关系,而折叠本质上是核心图上的商结构,可以独立取舍。移出之后有个直接收益——核心层不再背负「禁止递归交集」这条公理,因为核心层根本没有包含关系。
折叠的定义是:把模块内部的一堆点整个捏成一个点,对外只留下一张价目表——从哪几组入口进来、各要花多少钱。这里超图表示自动吃掉了上一版的一项条件:入口的 AND/OR 组合语义不需要专门保留,因为边界本来就是一组超边。
代价是那个爆炸原样出现了:边界超边的条数等于边界公式的析取范式项数。 取 $\bigwedge_{i=1}^{k}(I_{i,1} \vee I_{i,2})$,合取式仅 $2k$ 个文字,析取范式却有 $2^{k}$ 项——$k = 12$ 时需要 4096 条边界超边。这不是实现瑕疵,而是超图只能表达析取范式这一事实的固有代价。所以折叠必须设硬预算,超限就拒绝折叠、让子图保持展开。
更有意思的是,上一版里我凭直觉立的「禁止递归交集」公理,现在有了确切身份。我做了三组实测:
| 情形 | 实测 | 结论 |
|---|---|---|
| 单出口成立,模块内部超边不被外部复用 | 精确 7,折叠后 7 | 保真 |
| 外部直接消费模块内部点 | 精确 5;丢弃该外部边则目标不可达,改指向根则得 7 | 可推导性丢失,单出口是必要条件 |
| 两个模块共用内部超边 | 精确 12,折叠后 22 | 共享前置被重复计费,禁止交集是必要条件 |
三组的完整算例见 test_folding,析取范式规模的增长见 test_dnf_blowup。
也就是说,「禁止递归交集」不是一条随意的工程公理,而是折叠在集合成本下保真的必要条件。 同时它的适用范围被大幅收窄了:它只约束模块层的折叠操作,不约束核心图。 核心层的点可以任意共享前提、被任意多处引用,因为核心层压根不做折叠。上一版把它写成全局公理,是分层不清导致的过度约束。
图论里有个严格但很强的参考条件叫模块分解9:一个节点集合要能被商图节点无损替代,每个外部节点必须要么连接它的全部节点、要么一个也不连。我希望能封装的任意子图通常不满足这个条件,所以不能声称折叠在图论意义上天然无损。结论是保守的:先只提供折叠的校验器(判定单出口、无部分交集、析取范式规模),把折叠本身留到核心求解器稳定之后。
七节走完,回头对账。上一版三个最严重的问题都有了着落:没有专注数学本身,靠核心层准入判据解决;AND / OR 像补丁,被双射证明为可完全消除;无环限制表达上限,被最小不动点语义证明为多余,而且新语义比原来更宽松——好环得以保留,坏环自动失效。
但账不是白结的。新问题一并列出来:语义正确的成本口径 NP-hard 且不可近似,只能靠一对上下界夹逼;环从语法错误降级为静默失败,必须补诊断层,而诊断判据本身容易写错;理解倍率不能定义为路线计数;封装的边界公式可能指数爆炸;尾头相同但载荷不同的超边会被静默合并,超边必须带稳定身份;以及上一节那条反直觉的——图越接近想要的样子,就越靠近难解那一端。
至于上一篇里我当成头号未决问题的那个——权到底用什么可观测量来度量——现在回头看,我当时把它放错了层。
按前面立的准入判据,核心层只接收 $w$、不产生它,那么「$w$ 怎么算出来」按定义就在应用层。我却把它写成「模型的头号未决问题」,等于一边说它不属于模型、一边又说它是模型最大的洞。模型本身是完整的,它不需要关心 $w$ 从哪来。
应用层可选的方法很多:测实际有效学习时长;让 AI 按统一 Scale 打分;最朴素的直接数推导所需解释的文本长度。它们都能产出数字,落到实现层选一个即可。
真正剩下的不是「用什么可观测量」,而是「怎么让这些数加起来仍然有意义」——模型要对它们求和、并跨路线比较总和,这要求它们落在同一个可加的标度上。学习时长会被疲劳、动机、中断污染,而且你只观测得到走过的那条路,看不到反事实;AI 打分的分数通常非线性,「7 分」不是「1 分」的七倍;文本长度天然可加、但只是代理量,而且恰好撞上了上一篇警告过的陷阱:画一条没有论证的直线,文本很短、权很低,实际上根本没有推导。
但这件事自带反馈闭环:预测成本和实际结果可以对账,所以 $w$ 是可以被学出来、校准出来的。这是个机器学习问题,不是数学缺口。 冷启动用文本长度或 AI 打分当先验就够。
所以它应该被降级为:应用层的标定问题,有明确的解决路径。模型那一层现在是闭合的。
A. Tarski. A lattice-theoretical fixpoint theorem and its applications. Pacific Journal of Mathematics, 5(2):285–309, 1955. 保证单调算子在完全格上的最小不动点存在且唯一,是闭包良定义、且循环支撑自动失效的依据。 ↩︎ ↩︎
W. F. Dowling and J. H. Gallier. Linear-time algorithms for testing the satisfiability of propositional Horn formulae. Journal of Logic Programming, 1(3):267–284, 1984. 给出 Horn 公式可满足性的线性时间算法,本文的可达性判定即其计数式前向链接实现。 ↩︎
A. Martelli and U. Montanari. Additive AND/OR graphs. IJCAI 1973, pp. 1–11. 经典 AND/OR 图求解:AND 节点要求所有子问题共同解决,OR 节点从替代方案中选择,解是最低成本的解图而非路径。 ↩︎
G. Gallo, G. Longo, S. Pallottino, and S. Nguyen. Directed hypergraphs and applications. Discrete Applied Mathematics, 42(2–3):177–201, 1993. 有向超图的系统性处理,包括连通性、路径与割的定义,以及与命题逻辑、AND/OR 图的对应关系。 ↩︎
D. E. Knuth. A generalization of Dijkstra’s algorithm. Information Processing Letters, 6(1):1–5, 1977. 提出上位函数(superior function)条件,把 Dijkstra 推广到超图上的最小成本推导,是展开成本与深度成本可高效求解的依据。 ↩︎
U. Feige. A threshold of ln n for approximating set cover. Journal of the ACM, 45(4):634–652, 1998. 证明 $(1-o(1))\ln n$ 是集合覆盖近似的下界门槛,本文集合成本的不可近似性由归约传递而来。 ↩︎
G. Ausiello, G. F. Italiano, and U. Nanni. Hypergraph traversal revisited: Cost measures and dynamic algorithms. MFCS 1998, LNCS 1450. 指出超路径的最优化代价随所选度量函数,可以从线性时间一直变化到 NP-hard。 ↩︎
J. Goodman. Semiring parsing. Computational Linguistics, 25(4):573–605, 1999. 把推导的聚合统一到半环上,布尔半环给出可达性、$(\min,+)$ 给出最短推导、计数半环给出推导数量。 ↩︎
M. Habib and C. Paul. A survey of the algorithmic aspects of modular decomposition. Computer Science Review, 4(1):41–59, 2010. 模块分解给出「子图可被商图节点无损替代」的严格条件,本文用它说明任意子图的折叠不能默认无损。 ↩︎