练习:求集合Z中元素加起来的最小数目为4285.其中 Z = { w(i): w(n) - n^2 - n + 1, i = 1,2,...,30 }
我创造了一个解决方案。
def f(t):
return t ** 2 - t + 1
opt = z3.Optimize()
x = IntVector('x', 30)
x_val = [And(x[i] >= 0, x[i] <= 1) for i in range(30)]
opt.add(x_val)
m = [x[i] * f(i + 1) for i in range(30)]
m_sum = z3.Sum(m)
opt.add(m_sum == 4285)
opt.minimize(z3.Sum(x))
if z3.sat == opt.check():
model = opt.model()
print(model)
但效果太慢 只对小数有效。我怎样才能改进它呢?
在z3中用整数来表示布尔值几乎总是一个坏主意。所以,与其使用一个整数向量,在那里你说项目在0-1之间,不如简单地使用布尔向量和 If
构建。就像这样。
from z3 import *
def f(t):
return t ** 2 - t + 1
opt = z3.Optimize()
x = BoolVector('x', 30)
m = [If(x[i], f(i + 1), 0) for i in range(30)]
m_sum = z3.Sum(m)
opt.add(m_sum == 4285)
opt.minimize(z3.Sum([If(x[i], 1, 0) for i in range(30)]))
if z3.sat == opt.check():
model = opt.model()
print(model)
当我运行这个的时候,它很快就找到了解决方案。 When I run this, it goes really quickly and finds the solution:
[x__0 = False,
x__1 = False,
x__2 = False,
x__3 = False,
x__4 = False,
x__5 = False,
x__6 = False,
x__7 = False,
x__8 = False,
x__9 = False,
x__10 = False,
x__11 = False,
x__12 = False,
x__13 = True,
x__14 = False,
x__15 = False,
x__16 = False,
x__17 = True,
x__18 = False,
x__19 = False,
x__20 = False,
x__21 = False,
x__22 = False,
x__23 = False,
x__24 = False,
x__25 = True,
x__26 = True,
x__27 = True,
x__28 = True,
x__29 = True]
我没有检查这是否是正确的解决方案, 但至少它应该让你开始!
我尝试了以下MiniZinc模型。
int: n = 30;
set of int: N = 1..n;
function int: f(int: t) =
t*t - t + 1;
array[N] of var bool: x;
constraint ( 4285 == sum([x[i] * f(i) | i in N]) );
var int: bitCount = sum([ x[i] | i in N]);
solve minimize bitCount;
output ["#\(bitCount): "] ++
["\(if x[i] then 1 else 0 endif)" | i in N];
结果:
#7: 000000000000000010001001011011