草稿
电路可满足性
判断是否存在某个布尔赋值让电路输出 1,并理解一个赋值为什么是可高效检查的证书。
从点亮一盏灯开始
想象一个布尔控制电路(Boolean circuit)。它有四个输入开关 x1、x2、x3、x4。每个开关只能是 0 或 1。逻辑门(gate)如 AND、OR、NOT 会组合这些 bit,最后一条输出线 z 控制一盏灯。
判定问题(decision problem)是:
是否存在某个开关设置,让
z = 1?
本页始终使用同一个固定电路:
n1 = NOT x2
g1 = x1 AND n1
g2 = x3 OR x4
g3 = g1 AND g2
g4 = x2 AND x4
z = g3 OR g4
赋值字符串按位置读取:1010 表示 x1=1、x2=0、x3=1、x4=0。
第一个朴素想法:试完所有行
最直接的方法是列出每个赋值,并在每一行上计算电路输出。
四输入电路有 16 个格式正确的行。表格展示其中一些代表行。
x1=1 且 x2=0 让 g1=1;x3=1 让 g2=1。
一个拒绝行只说明这一行失败,不说明整个判定问题是 No。
四个输入只有 16 行,所以暴力搜索看起来还可以接受。但这张表不是证书。它是求解器盲目搜索的空间。
x1=1 且 x2=0 让 g1=1;x3=1 让 g2=1。
x2=1 且 x4=1 让 g4=1,因此 z=1。
这一行失败,但其他行仍可能点亮电路。
痛点:每加一个输入就翻倍
每增加一个输入,候选行数都会翻倍:
像 0000 这样的失败行,只能证明 0000 失败。它不能证明不存在满足行。
检查一行只沿电路走一次;盲目搜索可能要对每一行重复检查。
暴力表中的所有行
检查 bit,计算逻辑门,读取 z
同样增长形状,输入更大的思想实验
| n | 2^n | 一次检查标签 |
|---|---|---|
| 4 | 16 | O(|C| + n) |
| 8 | 256 | O(|C| + n) |
| 12 | 4,096 | O(|C| + n) |
| 20 | 1,048,576 | O(|C| + n) |
| 30 | 1,073,741,824 | O(|C| + n) |
核心发明:一行可以成为证书
现在把两件事分开:
- 搜索(search):问是否存在某个满足行。
- 验证(verification):接收一行候选赋值并检查它。
如果有人交给我们 1010,我们不需要再次搜索整张表。验证器(verifier)先检查它是否给每个输入一位,再按顺序计算逻辑门,最后看 z 是否为 1。
如果它让 z=1,则实例是 Yes。如果它失败,只说明这个证书失败。
证书(certificate)是一条被声称的见证。它成功时可以证明 Yes 答案,但它本身不是“找到自己”的方法。
验证器先检查证书格式,再按拓扑顺序计算逻辑门,最后读取 z。
已存储的值逐步增加:每个新的门值都在其输入已知之后加入。
| 步骤 | 工作 | 状态 |
|---|---|---|
| 验证通过 | 赋值为每个输入提供一位 | 当前 |
| n1: NOT | x2=0 -> n1=1 | 等待 |
| g1: AND | x1=1, n1=1 -> g1=1 | 等待 |
| g2: OR | x3=1, x4=0 -> g2=1 | 等待 |
| g3: AND | g1=1, g2=1 -> g3=1 | 等待 |
| g4: AND | x2=0, x4=0 -> g4=0 | 等待 |
| z: OR | g3=1, g4=0 -> z=1 | 等待 |
| 读取最终输出 | 最终输出 z 为 1,因此验证器接受 | 等待 |
Circuit-SAT 的形式化定义
布尔电路是一个有限的有向无环网络,由输入 bit、逻辑门和导线组成。满足赋值(satisfying assignment)是让输出门等于 1 的赋值。
令 $C$ 表示一个有 $n$ 个输入变量的布尔电路,$a \in \{0,1\}^n$ 表示一个赋值,$C(a)$ 表示该赋值下的电路输出。
直观地说:如果至少有一种输入设置能点亮输出灯,这个电路就是可满足的。
一个有限的有向无环布尔电路
每个输入对应一位
输出灯被点亮
验证器实现草图
验证器的职责比求解器窄:
function verifyCircuitSat(C, certificate) {
if (!hasOneBitPerInput(C, certificate)) return false;
const values = readInputBits(C, certificate);
for (const gate of gatesInTopologicalOrder(C)) {
values[gate.id] = applyBooleanGate(gate, values);
}
return values[C.output] === 1;
}
对格式正确的赋值:
像 101 或 1020 这样的格式错误字符串,会在逻辑门计算前被拒绝,所以不把它们当作有意义的电路运行。
| 步骤 | 读取输入 | 存储输出 |
|---|---|---|
| n1 = NOT | x2=0 | n1=1 |
| g1 = AND | x1=1, n1=1 | g1=1 |
| g2 = OR | x3=1, x4=0 | g2=1 |
| g3 = AND | g1=1, g2=1 | g3=1 |
| g4 = AND | x2=0, x4=0 | g4=0 |
| z = OR | g3=1, g4=0 | z=1 |
| 读取 z | z=1 | accept |
逻辑门顺序不变量
电路没有环,所以可以按拓扑顺序(topological order)计算:
validation -> n1 -> g1 -> g2 -> g3 -> g4 -> z -> read final output。
不变量(invariant)是:
每计算完一个逻辑门,已经存储的每个值都等于该输入或逻辑门在当前赋值下的真实布尔值。
这就是验证器可以很简单的原因:它不会猜测缺失的线值,只读取已经计算好的依赖。
复杂度
证书每个输入只有一位:
一次验证会检查赋值、计算逻辑门、沿导线读取依赖、最后读取输出:
盲目搜索可能要对每个赋值都重复一次检查:
每个输入一位
拓扑顺序
依赖值已经已知
这证明了 Circuit-SAT in NP:Yes 实例有多项式大小证书,而且验证器在多项式时间内运行。
主张边界
Circuit-SAT 还有一个著名角色:根据 Cook-Levin 定理,它是 NP-hard。本节点只命名这个定理,并预告为什么 Circuit-SAT 会成为后续归约的源问题。
这个小电路、被高亮的满足行、验证器追踪,都没有证明 NP-hardness。它们只证明成员关系这一侧:证书可以被高效检查。
| 主张 | 处理方式 | 原因 |
|---|---|---|
| Circuit-SAT 属于 NP | 本页证明 | 赋值长度为 n;验证器运行时间为 O(|C| + n) |
| Circuit-SAT 是 NP-hard | 本页只命名 | Cook-Levin 定理预告;本页不展开证明 |
| 小例子证明困难性 | 错误 | 小例子展示检查,不展示全称归约 |
常见混淆
每个修正都把搜索、检查和困难性主张分开。
`0000` 输出 0,所以也许电路不可满足。
修正: 失败行只排除这个赋值。No 证明需要排除所有行。
这个小追踪证明了 Circuit-SAT 是 NP-hard。
验证器会自动找到满足赋值。
太短或含非 bit 的字符串也可以送进逻辑门。
图谱连接
p-vs-np 提供证书和验证器的语言。np-hardness 提供“已知源问题可以启动后续归约链”的思想。
Circuit-SAT 是一般定义之后的第一个具体源问题。后续节点可以把 Circuit-SAT 转向 SAT 形式,但这些节点在真正存在前不会成为图谱链接。
证书与验证器
源问题角色
第一个具体源
后续转换,暂不链接
练习
区分行、格式错误证书,以及“已证明/只命名”的主张边界。
选择一个答案来检查。
选择一个答案来检查。
选择一个答案来检查。
选择一个答案来检查。
选择一个答案来检查。
- 在赋值
1010中,哪个逻辑门最先让它看起来有希望? - 为什么
0000不能证明整个电路不可满足? - 为什么
101应该在任何逻辑门计算前停止? - 本页证明了哪条主张:
Circuit-SAT in NP、Circuit-SAT is NP-hard,还是二者都证明?
图谱连接 : 电路可满足性