人工智能:现代方法读书笔记(六)
第06章 约束满足问题 (Constraint Satisfaction Problems)
摘要:本章系统介绍了约束满足问题(CSP)的形式化定义、核心求解算法及其应用。CSP 通过变量、值域和约束三元组对组合搜索问题进行结构化建模,相比传统状态空间搜索能利用约束传播大幅剪枝。核心算法包括回溯搜索及其优化(前向检验、MRV、LCV)、弧一致性(AC-3/MAC)以及局部搜索(最小冲突)。本章还通过对比表格和典型应用案例(如地图着色、调度)阐明了不同技术的适用场景,并探讨了 CSP 与搜索、SAT 求解、约束优化等章节的理论关联,最后展望了 CSP 与大模型协同求解等前沿方向。
本章导读
本章将围绕约束满足问题(CSP)的核心内容展开,依次讲解以下四个部分:
- 1. CSP定义与建模:介绍CSP的形式化三元组(变量、值域、约束)以及状态、一致性、完备性等基本概念,为后续算法奠定基础。
- 2. 回溯搜索与启发式:讲解基础的深度优先回溯搜索,以及前向检验、MRV、度启发式、最少约束值等优化技术,提升搜索效率。
- 3. 约束传播与弧一致性:深入解析AC-3算法及其在回溯中的集成(MAC),通过全局约束传播大幅缩减搜索空间。
- 4. 局部搜索与扩展应用:探讨最小冲突等局部搜索方法,并简要介绍约束优化、软约束等扩展方向与实际应用场景。
1. 章节概述
本章将一类特殊组合搜索——约束满足问题(CSP)单独提炼:变量集合 + 各自值域 + 变量间约束。相比第 3 章显式状态空间,CSP 用“变量赋值”与“约束传播”大幅压缩搜索,是调度、布局、配置等现实问题的标准建模工具。
2. 关键概念与定义
- CSP 形式化:三元组 ⟨X, D, C⟩,其中 X 为变量集,D 为各变量值域,C 为约束集(每个约束限定若干变量的允许取值组合)。
- 状态(赋值):对部分/全部变量赋予值域中的值;一致(consistent)指不违反任何约束;完备(complete)指所有变量均已赋值。
- 约束类型:一元(单变量)、二元(两变量)、高阶(多变量);可分硬约束(必满足)与软约束(偏好,含代价)。
- 回溯搜索(Backtracking Search):DFS 式逐变量赋值,遇冲突即回溯。
- 前向检验(Forward Checking, FC):每赋值后删去邻居值域中导致冲突的值,若某变量值域空则剪枝。
- MRV(最小剩余值 / Minimum Remaining Values):优先选“当前合法值最少”的变量(最受限优先)。
- 度启发式(Degree Heuristic):优先选约束其他未赋值变量最多的变量。
- 最少约束值(Least Constraining Value):优先选留下他人选择余地最大的值。
- 弧一致性(Arc Consistency)/ AC-3:使每条弧(有向约束)方向上一端变量的每个值都能在另一端找到兼容值。
- MAC(Maintaining Arc Consistency):在回溯搜索中,每次赋值后运行 AC-3 维护全局弧一致性。
- 约束传播(Constraint Propagation):利用局部约束缩减变量域,减少后续搜索。
- 局部搜索求解 CSP:用最小冲突(min-conflicts)启发在赋值空间中迭代修正。
3. 核心理论与算法
CSP 与搜索的关系:状态 = 部分赋值;目标是找“完备且一致”的赋值。相比第 3 章,CSP 无需显式定义“行动”与“转移”,而是以“给某变量选值”为隐式行动,并借约束传播大幅剪枝。
回溯搜索(基础 DFS):
function Backtracking-Search(csp):
return Recursive-BT({}, csp)
function Recursive-BT(assignment, csp):
if assignment complete: return assignment
var ← Select-Unassigned-Variable(csp, assignment) # 用 MRV
for value in Order-Domain-Values(var, csp): # 用 LCV
if value consistent with assignment:
assignment[var] = value
inferences ← Inference(csp, var) # FC / MAC
if inferences ≠ failure:
result = Recursive-BT(assignment, csp)
if result ≠ failure: return result
assignment.remove(var); undo inferences
return failure
Python 实现示例:
def backtracking_search(csp):
"""
回溯搜索(基础DFS)求解CSP。
输入:csp对象,包含变量、值域和约束。
输出:一个完整且一致的赋值字典,若无解则返回None。
"""
return recursive_backtrack({}, csp)
def recursive_backtrack(assignment, csp):
# 如果赋值已完备,返回解
if len(assignment) == len(csp.variables):
return assignment
# 选择未赋值变量(此处简化为顺序选择,实际可用MRV)
var = select_unassigned_variable(csp, assignment)
按顺序尝试值域中的每个值(实际可用LCV排序)
for value in order_domain_values(var, csp, assignment):
# 检查该值是否与当前赋值一致
if is_consistent(var, value, assignment, csp):
assignment[var] = value
# 可在此处加入前向检验或MAC推理
result = recursive_backtrack(assignment, csp)
if result is not None:
return result
# 回溯:移除该赋值
del assignment[var]
return None
辅助函数示例(需根据具体CSP实现)
def select_unassigned_variable(csp, assignment):
简单实现:返回第一个未赋值的变量
for var in csp.variables:
if var not in assignment:
return var
return None
def order_domain_values(var, csp, assignment):
简单实现:返回该变量的值域列表
return csp.domains[var]
def is_consistent(var, value, assignment, csp):
检查该赋值是否满足所有相关约束
for constraint in csp.constraints[var]:
if not constraint(var, value, assignment):
return False
return True
前向检验(FC):在 Inference 步,对 var 的相邻未赋值变量,移除与刚赋值冲突的值;若某邻域变空 → 回溯(比纯回溯更早剪枝,但只做“局部”检查)。
MRV 启发式:选剩余合法值最少的变量,力图尽早暴露失败(失败优先原则),显著减少扩展节点。
AC-3 算法(弧一致性):
function AC-3(csp):
queue ← all arcs (Xi, Xj) in csp
while queue not empty:
(Xi, Xj) ← REMOVE(queue)
if Revise(csp, Xi, Xj): # Xi 中是否有值无法与 Xj 兼容
if size(Domain(Xi)) == 0: return failure
for each Xk ≠ Xj adjacent to Xi:
ADD((Xk, Xi), queue) # 重新检查受影响弧
return true
function Revise(csp, Xi, Xj):
revised = false
for x in Domain(Xi):
if no y in Domain(Xj) satisfies constraint(Xi,Xj):
delete x from Domain(Xi); revised = true
return revised
Python 实现示例:
def ac3(csp):
"""
AC-3算法:实现弧一致性,缩减变量值域。
输入:csp对象,包含变量、值域和二元约束。
输出:若成功使所有弧一致则返回True,否则返回False。
"""
# 初始化队列,包含所有弧 (Xi, Xj)
queue = [(Xi, Xj) for Xi in csp.variables for Xj in csp.neighbors[Xi]]
while queue:
Xi, Xj = queue.pop(0)
if revise(csp, Xi, Xj):
# 如果Xi的值域被清空,问题无解
if not csp.domains[Xi]:
return False
# 将受影响的弧重新加入队列
for Xk in csp.neighbors[Xi]:
if Xk != Xj:
queue.append((Xk, Xi))
return True
def revise(csp, Xi, Xj):
"""
检查弧(Xi, Xj)是否一致,不一致则从Xi的值域中删除无兼容值的值。
返回:是否对Xi的值域进行了修改。
"""
revised = False
# 遍历Xi值域的每个值
for x in list(csp.domains[Xi]):
# 检查是否存在Xj的值y满足约束
if not any(satisfies_constraint(Xi, x, Xj, y, csp) for y in csp.domains[Xj]):
csp.domains[Xi].remove(x)
revised = True
return revised
def satisfies_constraint(Xi, x, Xj, y, csp):
"""
检查赋值(Xi=x, Xj=y)是否满足Xi和Xj之间的约束。
实际实现需根据具体约束条件编写。
"""
# 示例:地图着色问题,相邻区域颜色不能相同
if (Xi, Xj) in csp.constraints:
return csp.constraints[(Xi, Xj)](x, y)
return True # 若无约束则默认满足
AC-3 使每条弧两端一致,最坏 O(ed^3)(e 弧数,d 最大域大小)。MAC = 回溯中每次赋值后调用 AC-3,是最高效的完备求解组合之一。
局部搜索:最小冲突算法(Min-Conflicts)
function Min-Conflicts(csp, max_steps):
assignment ← random complete assignment
for step = 1 to max_steps:
if assignment consistent: return assignment
var ← random variable in conflict
value ← argmin_{v} number-of-conflicts(var=v)
assignment[var] = value
return failure
Python 实现示例:
def min_conflicts(csp, max_steps=1000):
"""
最小冲突算法:通过局部搜索求解CSP。
输入:csp对象,最大迭代步数max_steps。
输出:一个完整且一致的赋值字典,若无解则返回None。
"""
# 1. 生成随机完整赋值
assignment = {}
for var in csp.variables:
assignment[var] = random.choice(csp.domains[var])
for step in range(max_steps):
# 2. 检查当前赋值是否一致(无冲突)
conflicted_vars = conflicted_variables(assignment, csp)
if not conflicted_vars:
return assignment # 找到解
# 3. 随机选择一个冲突变量
var = random.choice(conflicted_vars)
4. 选择使冲突数最小的值
conflict_counts = []
for value in csp.domains[var]:
assignment[var] = value
conflict_counts.append((count_conflicts(var, assignment, csp), value))
assignment[var] = random.choice(csp.domains[var]) # 恢复原值用于比较
找出最小冲突值(可能有多个,随机选一个)
min_conflict = min(conflict_counts, key=lambda x: x[0])[0]
best_values = [value for cnt, value in conflict_counts if cnt == min_conflict]
best_value = random.choice(best_values)
5. 更新赋值
assignment[var] = best_value
return None # 超过最大步数未找到解
def conflicted_variables(assignment, csp):
"""返回当前赋值中所有冲突的变量列表"""
conflicted = []
for var in csp.variables:
if count_conflicts(var, assignment, csp) > 0:
conflicted.append(var)
return conflicted
def count_conflicts(var, assignment, csp):
"""计算变量var在当前赋值下的冲突数"""
conflicts = 0
for neighbor in csp.neighbors[var]:
if neighbor in assignment:
if not satisfies_constraint(var, assignment[var], neighbor, assignment[neighbor], csp):
conflicts += 1
return conflicts
对大规模 CSP(如 N 皇后、调度)常比回溯更快找到解,但不保证完备。
约束类型扩展:软约束可用**约束优化问题(COP)**框架,给违反约束赋代价,目标最小化总代价(汇合第 3 章代价思想)。
4. 关键图示/表格说明
CSP 求解技术对比表:
| 技术 | 类型 | 作用 | 完备/最优 |
|---|---|---|---|
| 回溯搜索 | 深度优先 | 基线完备搜索 | 完备 |
| 前向检验 FC | 预剪枝 | 赋值后局部删值 | 完备、更快 |
| MRV / DH | 变量序 | 失败优先 | 不增完备性、减节点 |
| AC-3 / MAC | 约束传播 | 全局域缩减 | 完备、最强剪枝 |
| 最小冲突 | 局部搜索 | 迭代修正 | 不完备、快 |
弧一致性示意(文字):如 Australia 地图着色,某区域值被固定后,沿边界弧传播,相邻区域值域立即剔除该色;递归传播直至无弧可修。
典型应用建模(文字):
- 地图着色:变量=地区,域=颜色,约束=相邻异色。
- 调度:变量=任务,域=时段,约束=资源/先后。
- 电路/布局:变量=元件,域=位置,约束=不重叠/连线短。
5. 与其他章节的关联
- CSP 是第 3 章搜索的"结构化"特例:用变量/约束替代显式状态转移,约束传播替代盲目扩展。
- 回溯 + 启发式思想延伸至逻辑 SAT 求解与规划(第 7、11 章)。
- 软约束/代价化衔接约束优化与效用(第 2、16 章)。
- 局部搜索(最小冲突)复用第 4 章爬山/局部优化思路。
- 变量消除、传播算法与概率图模型(第 13–14 章变量消元)在结构上有对应。
6. 延伸思考
- CSP 与大模型约束生成:让 LLM 生成满足复杂业务规则(合规、排班、配置)的方案时,常违反隐性约束。能否把问题建模为硬/软 CSP,用 AC-3/MAC 在模型输出后做"可满足性校验与修正",构建可靠的人机协同求解?
- 可满足性与现实规模:现代 SAT/SMT 求解器已能处理百万级变量。当 AI 系统(如芯片设计、课程表、交通信号)依赖此类约束求解,如何平衡"求解速度"与"约束表达的完备性/可解释性"?
更多推荐




所有评论(0)