求最小总和
Find minimum sum
从每一行中找出一个元素的最小元素总和。我认为答案是
-214,但 z3py returns 不满意。怎么了?
from z3 import Solver, Int, ForAll, Or
ARR = [
[36, 12, 90, 88, 82],
[-92, 50, 40, 31, 43],
[81, 28, -26, 8, -59],
[18, -99, -70, -33, 58],
[44, -33, 24, -92, -68],
]
s = Solver()
xs = [Int(f"x_{i}") for i, row in enumerate(ARR)]
ys = [Int(f"y_{i}") for i, row in enumerate(ARR)]
for x, y, row in zip(xs, ys, ARR):
s.add(Or(*[x == val for val in row]))
s.add(Or(*[y == val for val in row]))
s.add(ForAll(ys, sum(xs) <= sum(ys)))
print(s.check()) # unsat
您的编码不太正确。如果您在程序中加入以下行:
print(s.sexpr())
除其他外,您会看到它会打印:
(assert (forall ((y_0 Int) (y_1 Int) (y_2 Int) (y_3 Int) (y_4 Int))
(<= (+ 0 x_0 x_1 x_2 x_3 x_4) (+ 0 y_0 y_1 y_2 y_3 y_4))))
这就是unsat
的原因。这是一个量化公式,因此它表示只有当公式为真时才可满足 对于所有 值 y_0
.. y_4
。这显然不是真的,因此 unsat
结果。
您应该使用 z3 的优化引擎来代替此公式。从每一行中选择一个变量,将它们相加,然后 minimize
结果。像这样:
from z3 import *
ARR = [
[36, 12, 90, 88, 82],
[-92, 50, 40, 31, 43],
[81, 28, -26, 8, -59],
[18, -99, -70, -33, 58],
[44, -33, 24, -92, -68],
]
o = Optimize()
es = [Int(f"e_{i}") for i, row in enumerate(ARR)]
for e, row in zip (es, ARR):
o.add(Or(*[e == val for val in row]))
minTotal = Int("minTotal")
o.add(minTotal == sum(es))
o.minimize(minTotal)
print(o.check())
print(o.model())
当我 运行 这个时,我得到:
sat
[e_0 = 12,
e_3 = -99,
e_2 = -59,
e_1 = -92,
e_4 = -92,
minTotal = -330]
也就是说,求解器从第一行选择 12
,从第二行选择 -92
,从第三行选择 -59
,从第四行选择 -99
,然后 -92
从最后一行开始;最小总和 -330
.
很容易看出这是正确的解决方案,因为求解器从每一行中选择最小元素,因此它们的总和也将是最小的。 (我不确定您为什么期望 -214
是答案。)
从每一行中找出一个元素的最小元素总和。我认为答案是 -214,但 z3py returns 不满意。怎么了?
from z3 import Solver, Int, ForAll, Or
ARR = [
[36, 12, 90, 88, 82],
[-92, 50, 40, 31, 43],
[81, 28, -26, 8, -59],
[18, -99, -70, -33, 58],
[44, -33, 24, -92, -68],
]
s = Solver()
xs = [Int(f"x_{i}") for i, row in enumerate(ARR)]
ys = [Int(f"y_{i}") for i, row in enumerate(ARR)]
for x, y, row in zip(xs, ys, ARR):
s.add(Or(*[x == val for val in row]))
s.add(Or(*[y == val for val in row]))
s.add(ForAll(ys, sum(xs) <= sum(ys)))
print(s.check()) # unsat
您的编码不太正确。如果您在程序中加入以下行:
print(s.sexpr())
除其他外,您会看到它会打印:
(assert (forall ((y_0 Int) (y_1 Int) (y_2 Int) (y_3 Int) (y_4 Int))
(<= (+ 0 x_0 x_1 x_2 x_3 x_4) (+ 0 y_0 y_1 y_2 y_3 y_4))))
这就是unsat
的原因。这是一个量化公式,因此它表示只有当公式为真时才可满足 对于所有 值 y_0
.. y_4
。这显然不是真的,因此 unsat
结果。
您应该使用 z3 的优化引擎来代替此公式。从每一行中选择一个变量,将它们相加,然后 minimize
结果。像这样:
from z3 import *
ARR = [
[36, 12, 90, 88, 82],
[-92, 50, 40, 31, 43],
[81, 28, -26, 8, -59],
[18, -99, -70, -33, 58],
[44, -33, 24, -92, -68],
]
o = Optimize()
es = [Int(f"e_{i}") for i, row in enumerate(ARR)]
for e, row in zip (es, ARR):
o.add(Or(*[e == val for val in row]))
minTotal = Int("minTotal")
o.add(minTotal == sum(es))
o.minimize(minTotal)
print(o.check())
print(o.model())
当我 运行 这个时,我得到:
sat
[e_0 = 12,
e_3 = -99,
e_2 = -59,
e_1 = -92,
e_4 = -92,
minTotal = -330]
也就是说,求解器从第一行选择 12
,从第二行选择 -92
,从第三行选择 -59
,从第四行选择 -99
,然后 -92
从最后一行开始;最小总和 -330
.
很容易看出这是正确的解决方案,因为求解器从每一行中选择最小元素,因此它们的总和也将是最小的。 (我不确定您为什么期望 -214
是答案。)