【发布时间】:2020-10-23 19:43:35
【问题描述】:
我正在尝试创建一个具有以下约束的矩阵。
- 列总和应介于 300 和 390 之间,包括两个值。
- 行总和应等于用户为每行指定的值。
- 矩阵中的非零值不应小于 10。
- 给定列中非零值的计数不应超过 4。
- 列应按对角线顺序排列。
如果UserInput = [427.7, 12.2, 352.7, 58.3, 22.7, 31.9, 396.4, 29.4, 171.5, 474.5, 27.9, 200]
我想要这样的输出矩阵,
编辑 1
我使用 Pyomo 尝试了以下方法,但是,我遇到了第 5 个约束,即 列值应在矩阵中对角对齐
import sys
import math
import numpy as np
import pandas as pd
from pyomo.environ import *
solverpath_exe= 'glpk-4.65\\w64\\glpsol.exe'
solver=SolverFactory('glpk',executable=solverpath_exe)
# Minimize the following:
# Remaining pieces to be zero for all et values
# The number of cells containg non-zero values
# Constraints
# 1) Column sum, CS, is: 300 <= CS <= 390
# 2) Row sum, RS, is equal to user-specified values, which are present in the E&T ticket column of the file
# 3) Number of non-zero values, NZV, in each column, should be: 0 < NZV <= 4
# 4) The NZV in the matrix should be: NZV >= 10
# 5) The pieces are stacked on top of each other. So, a the cell under a non-zero value cell is zero, than all cells underneath should have zeros.
maxlen = 390
minlen = 300
npiece = 4
piecelen = 10
# Input data: E&T Ticket values
etinput = [427.7, 12.2, 352.7, 58.3, 22.7, 31.9,
396.4, 29.4, 171.5, 474.5, 27.9, 200]
# Create data structures to store values
etnames = [f'et{i}' for i in range(1,len(etinput) + 1)]
colnames = [f'col{i}' for i in range(1, math.ceil(sum(etinput)/minlen))] #+1 as needed
et_val = dict(zip(etnames, etinput))
# Instantiate Concrete Model
model2 = ConcreteModel()
# define variables and set upper bound to 390
model2.vals = Var(etnames, colnames, domain=NonNegativeReals,bounds = (0, maxlen), initialize=0)
# Create Boolean variables
bigM = 10000
model2.y = Var(colnames, domain= Boolean)
model2.z = Var(etnames, colnames, domain= Boolean)
# Minimizing the sum of difference between the E&T Ticket values and rows
model2.minimizer = Objective(expr= sum(et_val[r] - model2.vals[r, c]
for r in etnames for c in colnames),
sense=minimize)
model2.reelconstraint = ConstraintList()
for c in colnames:
model2.reelconstraint.add(sum(model2.vals[r,c] for r in etnames) <= bigM * model2.y[c])
# Set constraints for row sum equal to ET values
model2.rowconstraint = ConstraintList()
for r in etnames:
model2.rowconstraint.add(sum(model2.vals[r, c] for c in colnames) <= et_val[r])
# Set contraints for upper bound of column sums
model2.colconstraint_upper = ConstraintList()
for c in colnames:
model2.colconstraint_upper.add(sum(model2.vals[r, c] for r in etnames) <= maxlen)
# Set contraints for lower bound of column sums
model2.colconstraint_lower = ConstraintList()
for c in colnames:
model2.colconstraint_lower.add(sum(model2.vals[r, c] for r in etnames) + bigM * (1-model2.y[c]) >= minlen)
model2.bool = ConstraintList()
for c in colnames:
for r in etnames:
model2.bool.add(model2.vals[r,c] <= bigM * model2.z[r,c])
model2.npienceconstraint = ConstraintList()
for c in colnames:
model2.npienceconstraint.add(sum(model2.z[r, c] for r in etnames) <= npiece)
# Call solver for model
solver.solve(model2);
# Create dataframe of output
pdtest = pd.DataFrame([[model2.vals[r, c].value for c in colnames] for r in etnames],
index=etnames,
columns=colnames)
pdtest
输出
【问题讨论】:
-
对我来说这看起来像是一个 NP 完全问题。如果您已经知道哪些近对角线元素不为零,则它是线性方程组(
np.linalg.lstsq表示列和 395),但您必须迭代组合。你有 17 个方程(12 个硬方程和 7 个软方程),其中有 10 个未知数,但你可以选择未知数,所以你有大量可数的方程组,你可以选择满足条件的方程组。一般来说,除非你很幸运,否则这是不可能的。 -
嗨,韩感谢您的回复。你有什么“蛮力”方法来解决这个问题吗?
-
定义哪些元素可以被认为是非零的,例如每列 4 个,构成 28 个元素。遍历从 0 到
2**28-1的所有整数。跳过不满足非零元素数量要求的那些。其他使用np.linalg.lstsq。丢弃不符合其他要求的解决方案。但是暴力破解2**28组合看起来不是一个好主意。对于此类问题,最好查看simulated annealing。 -
韩,你能给我一段使用np.linalg.lstsq func的代码吗?这肯定会帮助我开始使用这种方法。
-
你能更具体地说明约束 5 的含义吗?我在代码注释中看到您希望对列中的值块强制执行一些完整性,但它并没有说明与对角线的关系?
标签: python numpy optimization pyomo constraint-programming