You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Z3中以Max-SMT形式编码频率分配问题及相关疑问

频率分配Max-SMT编码问题解答

先直接回应你的两个疑问,再分析代码结果偏离最优值的原因,最后给出修正思路:

问题1:软约束的转化是否正确?

是的,你理解的核心逻辑是对的——把原软约束C转化为C ∨ p(p是新增的违反标记命题变量),再通过p关联违反成本。不过不用手动写这么繁琐:Z3的Optimize.add_soft()方法已经封装了这个逻辑,它会自动引入辅助变量、处理成本累加,你只需要传入原软约束表达式和对应的权重即可,和你代码里用add_soft()的方式是一致的,不用额外手动构造(C ∨ p)的子句。

问题2:如何编码实现成本最小化并查看结果?

Z3的Optimize模块默认会把所有add_soft()传入的权重作为目标,自动最小化违反软约束的权重总和。要查看最终成本,只需要在s.check()返回sat后,通过s.objectives()获取目标表达式,再用模型evaluate()计算具体数值即可。不过你的代码结果偏离最优值,核心问题出在约束编码错误和变量重复定义上,而非成本计算的逻辑。


代码结果偏离最优值的核心问题

1. 硬约束编码错误

你处理硬约束|fi - fj|=d时,错误地使用了软约束的上下界逻辑(比如mi - mj >= floor(...) +1),但硬约束需要的是精确等式:

  • 比如ti ∧ ¬tj场景下,fi=2+14mi,fj=8+14mj,所以|(2+14mi)-(8+14mj)|=d等价于|14(mi-mj)-6|=d,也就是14(mi-mj)-6 = d 或 14(mi-mj)-6 = -d,对应整数解mi-mj=(d+6)/14或mi-mj=(-d+6)/14,而不是你写的大于等于/小于等于的或条件。
  • 其他ti/tj组合的硬约束也存在类似的错误,这会导致硬约束被错误放宽,进而让求解器找到不符合原始问题要求的解,最终软约束违反成本偏高。

2. 变量重复定义

你在循环每个约束时,都重新定义ti = Bool('t_%d' % ctr.first_var)和mi = Int('m_%d' % ctr.first_var),这会导致同一个变量被多次声明,虽然Z3不会报错,但可能出现约束叠加冲突或赋值混乱的问题。正确的做法是提前初始化所有变量,用字典存储每个变量的Z3对象,避免重复定义。


修正后的核心代码示例

import mongoengine as me
from models import Graph
from z3 import *
from math import ceil, floor

me.connect(db='tcc')
instance = next(Graph.objects(name='scen06'))
Vars = instance.var
Doms = instance.dom
Ctrs = instance.ctr
weights = { 1 : 1000, 2 : 100, 3 : 10, 4 : 1 }

# 提前初始化所有变量,避免重复定义
t_vars = {v: Bool(f't_{v}') for v in Vars}
m_vars = {v: Int(f'm_{v}') for v in Vars}

s = Optimize()

# 先添加所有变量的域约束(只执行一次)
for v in Vars:
    ti = t_vars[v]
    mi = m_vars[v]
    s.add(Implies(ti, Or(And(1 <= mi, mi <= 11), And(18 <= mi, mi <= 28))))
    s.add(Implies(Not(ti), Or(And(29 <= mi, mi <= 39), And(46 <= mi, mi <= 56))))

# 处理每个约束
for ctr in Ctrs:
    v1 = ctr.first_var
    v2 = ctr.second_var
    ti = t_vars[v1]
    tj = t_vars[v2]
    mi = m_vars[v1]
    mj = m_vars[v2]
    k = ctr.deviation
    current_weight = weights.get(ctr.weight, 0)

    if ctr.operator == '=':
        # 硬约束:|fi - fj| = k,分场景编码精确等式
        # ti ∧ ¬tj: |2+14mi - (8+14mj)| = k → |14(mi-mj) -6| = k
        eq1 = 14*(mi - mj) - 6 == k
        eq2 = 14*(mi - mj) - 6 == -k
        s.add(Implies(And(ti, Not(tj)), Or(eq1, eq2)))
        
        # ¬ti ∧ tj: |(8+14mi) - (2+14mj)| = k → |14(mi-mj) +6| = k
        eq3 = 14*(mi - mj) + 6 == k
        eq4 = 14*(mi - mj) + 6 == -k
        s.add(Implies(And(Not(ti), tj), Or(eq3, eq4)))
        
        # ti ∧ tj 或 ¬ti ∧ ¬tj: |14(mi-mj)| = k
        eq5 = 14*(mi - mj) == k
        eq6 = 14*(mi - mj) == -k
        s.add(Implies(And(ti, tj), Or(eq5, eq6)))
        s.add(Implies(And(Not(ti), Not(tj)), Or(eq5, eq6)))

    elif ctr.operator == '>':
        # 软约束:|fi - fj| > k,分场景编码线性约束
        # ti ∧ ¬tj场景
        cond1 = mi - mj >= floor((k + 6)/14) + 1
        cond2 = mi - mj <= ceil((-k + 6)/14) - 1
        s.add_soft(Implies(And(ti, Not(tj)), Or(cond1, cond2)), current_weight)
        
        # ¬ti ∧ tj场景
        cond3 = mi - mj >= floor((k - 6)/14) + 1
        cond4 = mi - mj <= ceil((-k - 6)/14) - 1
        s.add_soft(Implies(And(Not(ti), tj), Or(cond3, cond4)), current_weight)
        
        # ti ∧ tj 和 ¬ti ∧ ¬tj场景
        cond5 = mi - mj >= floor(k/14) + 1
        cond6 = mi - mj <= ceil(-k/14) - 1
        s.add_soft(Implies(And(ti, tj), Or(cond5, cond6)), current_weight)
        s.add_soft(Implies(And(Not(ti), Not(tj)), Or(cond5, cond6)), current_weight)

# 求解并输出结果
result = s.check()
print(f"求解结果: {result}")
if result == sat:
    m = s.model()
    # 获取最小总成本(违反软约束的权重总和)
    total_cost = m.evaluate(s.objectives()[0])
    print(f"最小总成本: {total_cost}")
    # 可选:查看每个变量的赋值
    for v in Vars:
        print(f"变量{v}: t={m.evaluate(t_vars[v])}, m={m.evaluate(m_vars[v])}")

内容的提问来源于stack exchange,提问作者Yuri Carvalho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:38:20