我有一个与工作相关的问题,我正在尝试使用 GEKKO 等优化包来解决。我正在尝试平衡 3 个整数箱,以便它们的总和尽可能接近(如果不相等的话)。
我正在尝试使用 GEKKO 在 Python 中编写此代码,但这是我第一次尝试使用此类包,其中很多内容超出了我的理解范围。理想情况下,我希望将重新平衡的垃圾箱作为输出,以查看平衡是如何完成的。我已经写了下面的内容来开始。它并不完整,但我只是想边写边写下概念。
下面是我一直在尝试使用的一些示例 bin 和 ID:
bin1 = [1,2,2,1,1,1,3,1]
bin2 = [2,2,1,1,3,1,1]
bin3 = [2,1,2,1,1,1,1]
bin1_IDs=[15,28,39,111,202,300,411,415]
bin2_IDs=[2,10,56,117,132,189,367]
bin3_IDs=[44,102,211,227,351,389,406]
# Original bin contents and their sums
bins = [bin1, bin2, bin3]
original_sums = [sum(bin) for bin in bins]
num_bins = len(bins)
num_items = sum(len(bin) for bin in bins)
# Create GEKKO model
m = GEKKO(remote=False)
# Decision variables
# x[i][j] = 1 if item i is in bin j, 0 otherwise
# Create binary decision variables
x = [[m.Var(integer=True, lb=0, ub=1) for j in range(num_bins)] for i in range(num_items)]
# Ensure each item is assigned to exactly one bin
for i in range(num_items):
m.Equation(sum(x[i][j] for j in range(num_bins)) == 1)
# Solve the optimization problem
m.solve(disp=True)
# Output the results
for j in range(num_bins):
print(f"Bin {j+1} contents: ", end="")
for i in range(num_items):
if x[i][j].value[0] > 0.5: # Item assigned to bin j
print(items[i], end=" ")
这里有一个使用
pyomo
的破解方法,它是线性编程的替代包。 我确信如果需要的话可以将其转录为 GEKKO,我只是不太熟悉 GEKKO 的语法。
这基本上是一个大型的二进制整数程序,其中二进制变量表示每个项目的移动(或不移动),因此关键变量是
x[item, orig, final]
,其中orig, final
对被简化为仅可能的值该项目可以更严格地限制问题。
目标的关键指标是最小最大,其中我们减少最高容器的值,因此容器值被均匀地“压缩”。 移动会受到轻微的惩罚,必须仔细权衡最大仓值。
值得注意的是,如果您的“实际问题”比这大得多,可能需要其他技术,因为这可能无法扩展到巨大的问题规模,但它可以在一瞬间解决这个问题
import pyomo.environ as pyo
# DATA
bin1 = [1, 2, 2, 1, 1, 1, 3, 1]
bin2 = [2, 2, 1, 1, 3, 1, 1]
bin3 = [2, 1, 2, 1, 1, 1, 1]
# bin1 = [0, 0, 0, 0, 0, 0, 0, 0]
bin1_IDs = [15, 28, 39, 111, 202, 300, 411, 415]
bin2_IDs = [2, 10, 56, 117, 132, 189, 367]
bin3_IDs = [44, 102, 211, 227, 351, 389, 406]
values = {}
values.update(dict(zip(bin1_IDs, bin1)))
values.update(dict(zip(bin2_IDs, bin2)))
values.update(dict(zip(bin3_IDs, bin3)))
orig_loc = {}
orig_loc.update({item: "b1" for item in bin1_IDs})
orig_loc.update({item: "b2" for item in bin2_IDs})
orig_loc.update({item: "b3" for item in bin3_IDs})
bins = ["b1", "b2", "b3"]
poss_moves = {
item: {(orig, other) for other in bins} for item, orig in orig_loc.items()
}
all_moves = {(b, bb) for b in bins for bb in bins}
non_moves = {(b, b) for b in bins}
# MODEL
m = pyo.ConcreteModel("bin_shuffler")
# SETS
m.I = pyo.Set(initialize=values.keys(), doc="items")
m.B = pyo.Set(initialize=bins, doc="bin")
m.M = pyo.Set(within=m.B * m.B, initialize=all_moves, doc="moves")
# indexed set of the item : possible moves for that item
m.IM = pyo.Set(m.I, domain=m.M, initialize=poss_moves, doc="possible moves")
# a "flat" set of same for use in variable indexing
m.IM_flat = pyo.Set(
domain=m.I * m.M, initialize={(item, move) for item in m.IM for move in m.IM[item]}
)
# PARAMS
m.value = pyo.Param(m.I, initialize=values)
move_cost = 0.01 # very small weight such that it never eclipses the max_bin value objective
# VARS
# item i makes move m
m.x = pyo.Var(m.IM_flat, domain=pyo.Binary)
m.max_value = pyo.Var()
m.move_count = pyo.Var()
# OBJ
# mini-max: minimize the max sum of largest bin, and minimize moves w/ small penalty
m.obj = pyo.Objective(expr=m.max_value + move_cost * m.move_count, sense=pyo.minimize)
# CONSTRAINTS
# all items must make a move
@m.Constraint(m.I)
def make_move(m, i):
return sum(m.x[i, move] for move in m.IM[i]) == 1
# count all of the moves that are not non-moves
m.set_move_count = pyo.Constraint(
expr=m.move_count
>= sum(m.x[i, move] for i in m.I for move in m.IM[i] if move not in non_moves)
)
# catch the max bin value
@m.Constraint(m.B)
def max_bin(m, bin):
return m.max_value >= sum(
m.value[i] * m.x[i, orig, final]
for i in m.I
for (orig, final) in m.IM[i]
if final == bin
)
# inspect it...
m.pprint()
# solve it...
opt = pyo.SolverFactory("appsi_highs")
res = opt.solve(m, tee=True)
# report it...
if pyo.check_optimal_termination(res):
print(res)
m.display()
for i in m.I:
for orig, final in m.IM[i]:
if pyo.value(m.x[i, orig, final]) > 0.5 and orig != final:
print(f"move {i} from {orig} to {final}")
for b in m.B:
# get the sum
bin_total = sum(
m.value[i] * pyo.value(m.x[i, orig, final])
for i in m.I
for (orig, final) in m.IM[i]
if final == b
)
print(f"total value of bin {b}: {bin_total}")
else:
print("solve failed")
move 415 from b1 to b3
total value of bin b1: 11.0
total value of bin b2: 11.0
total value of bin b3: 10.0