使用 PuLP 进行线性规划沙拉混合物优化
Linear programming salad mixture optimization with PuLP
我已经设置了以下 LP 问题,除了我对沙拉蔬菜的质量百分比限制外,一切似乎都有效。我希望沙拉蔬菜的质量至少为 40%,但 PuLP 的 lpSum
出现语法错误,我不确定如何协调它。
我对每种沙拉都有以下限制:
至少 15 克蛋白质
至少 2 克,最多 8 克脂肪
至少 4 克碳水化合物
最多 200 毫克钠
至少 40% 的绿叶蔬菜。
from pulp import *
# Creates a list of the Ingredients
Ingredients = ['TOMATO', 'LETTUCE', 'SPINACH', 'CARROT', 'SUNFLOWER', 'TOFU', 'CHICKPEAS', 'OIL']
kcal = {'TOMATO': 21,
'LETTUCE': 16,
'SPINACH': 40,
'CARROT': 41,
'SUNFLOWER': 585,
'TOFU': 120,
'CHICKPEAS': 164,
'OIL': 884}
protein = {'TOMATO': 0.85,
'LETTUCE': 1.62,
'SPINACH': 2.86,
'CARROT': 0.93,
'SUNFLOWER': 23.4,
'TOFU': 16,
'CHICKPEAS': 9,
'OIL': 0}
fat = {'TOMATO': 0.33,
'LETTUCE': 0.20,
'SPINACH': 0.39,
'CARROT': 0.24,
'SUNFLOWER': 48.7,
'TOFU': 5.0,
'CHICKPEAS': 2.6,
'OIL': 100.0}
carbs = {'TOMATO': 4.64,
'LETTUCE': 2.37,
'SPINACH': 3.63,
'CARROT': 9.58,
'SUNFLOWER': 15.0,
'TOFU': 3.0,
'CHICKPEAS': 27.0,
'OIL': 0.0}
sodium = {'TOMATO': 9.0,
'LETTUCE': 28.0,
'SPINACH': 65.0,
'CARROT': 69.0,
'SUNFLOWER': 3.80,
'TOFU': 120.0,
'CHICKPEAS': 78.0,
'OIL': 0.0}
cost = {'TOMATO': 1.0,
'LETTUCE': 0.75,
'SPINACH': 0.50,
'CARROT': 0.50,
'SUNFLOWER': 0.45,
'TOFU': 2.15,
'CHICKPEAS': 0.95,
'OIL': 2.00}
# Create the 'prob' variable to contain the problem data
prob = LpProblem("The Salad Problem", LpMinimize)
# A dictionary called 'ingredient_vars' is created to contain the referenced Variables
ingredient_vars = LpVariable.dicts("Ingr",Ingredients,0)
# The objective function is added to 'prob' first
prob += lpSum([kcal[i]*ingredient_vars[i] for i in Ingredients]), "Total kCal of Ingredients per salad"
# The constraints are added to 'prob'
prob += lpSum([protein[i] * ingredient_vars[i] for i in Ingredients]) >= 15.0, "ProteinRequirement"
prob += 8.0 >= lpSum([fat[i] * ingredient_vars[i] for i in Ingredients]) >= 2.0, "FatRequirement"
prob += lpSum([carbs[i] * ingredient_vars[i] for i in Ingredients]) >= 4.0, "CarbRequirement"
prob += lpSum([sodium[i] * ingredient_vars[i] for i in Ingredients]) <= 200.0, "SodiumRequirement"
prob += lpSum(prob.variables()[2].varValue + prob.variables()[4].varValue) / lpSum([prob.variables()[i].varValue for i in range(8)]) >= 0.40, "GreensRequirement"
prob.solve()
# The status of the solution is printed to the screen
print("Status:", LpStatus[prob.status])
# Each of the variables is printed with it's resolved optimum value
for v in prob.variables():
print(v.name, "=", v.varValue)
# The optimised objective function value is printed to the screen
print("Total kCal of Ingredients per salad = ", value(prob.objective))
这是给我带来问题的约束:
prob += lpSum(prob.variables()[2].varValue + prob.variables()[4].varValue) / lpSum([prob.variables()[i].varValue for i in range(8)]) >= 0.40, "GreensRequirement"
这会导致在 NoneType 上使用 + 运算符时出错,因为变量还没有值。我只是不确定如何设置这种约束。我已经查看了关于此的 PuLP 文档,但我还没有找到解决问题的运气。
一些评论
- 正如您自己所说,您不能建立基于某些尚未优化的决策变量的值的约束!
- 无法使用
.varValue
约束
- 我一般不明白
prob.variables()[2]
的概念,而不是像以前那样访问 ingredient_vars[i]
(但因为我不知道 prob.variables
[=82= 到底是什么] 我会忽略这个)
- 如果您替换
.varValue
访问并直接使用变量,您将构建一个带有 分部 的约束,它是非线性的,纸浆不会接受这个
- 即使纸浆接受这个,求解器也无法解决这个问题(LP-求解器)
做什么
我们需要:
- 建立一个线性约束来表示你的比率约束
理论
我们使用 lpsolve here 文档中描述的重新表述。摘录:
lpsolve 的文档截图 (http://lpsolve.sourceforge.net/5.1/ratio.htm)
在实践中
备注:由于我不熟悉变量的prob.variables()[2]
类访问并且懒得查看文档,我在这里猜测绿色蔬菜是:生菜+菠菜。如果我误解了这个,请随时更改它!
约束看起来是硬编码的(不太好,但对于您的示例来说可能没问题),如下所示:
prob += (0 - 0.4) * ingredient_vars['TOMATO'] + (1 - 0.4) * ingredient_vars['LETTUCE'] + \
(1 - 0.4) * ingredient_vars['SPINACH'] + (0 - 0.4) * ingredient_vars['CARROT'] + \
(0 - 0.4) * ingredient_vars['SUNFLOWER'] + (0 - 0.4) * ingredient_vars['TOFU'] + \
(0 - 0.4) * ingredient_vars['CHICKPEAS'] + (0 - 0.4) * ingredient_vars['OIL'] >= 0.0, 'GreensRequirement'
这是从 lpsolve 文档的公式直接翻译而来的。检查我们必须做出的假设!
示例输出
两个完全使用您的代码和替换约束的示例(考虑到我对绿色成分的假设):
比率为 0.4(如上面的代码所述)
('Status:', 'Optimal')
('Ingr_CARROT', '=', 0.0)
('Ingr_CHICKPEAS', '=', 0.0)
('Ingr_LETTUCE', '=', 0.58548009)
('Ingr_OIL', '=', 0.0)
('Ingr_SPINACH', '=', 0.0)
('Ingr_SUNFLOWER', '=', 0.0)
('Ingr_TOFU', '=', 0.87822014)
('Ingr_TOMATO', '=', 0.0)
('Total kCal of Ingredients per salad = ', 114.75409824)
比率为 0.9
('Status:', 'Optimal')
('Ingr_CARROT', '=', 0.0)
('Ingr_CHICKPEAS', '=', 0.0)
('Ingr_LETTUCE', '=', 4.4146501)
('Ingr_OIL', '=', 0.0)
('Ingr_SPINACH', '=', 0.0)
('Ingr_SUNFLOWER', '=', 0.0)
('Ingr_TOFU', '=', 0.49051668)
('Ingr_TOMATO', '=', 0.0)
('Total kCal of Ingredients per salad = ', 129.4964032)
我已经设置了以下 LP 问题,除了我对沙拉蔬菜的质量百分比限制外,一切似乎都有效。我希望沙拉蔬菜的质量至少为 40%,但 PuLP 的 lpSum
出现语法错误,我不确定如何协调它。
我对每种沙拉都有以下限制:
至少 15 克蛋白质
至少 2 克,最多 8 克脂肪
至少 4 克碳水化合物
最多 200 毫克钠
至少 40% 的绿叶蔬菜。
from pulp import *
# Creates a list of the Ingredients
Ingredients = ['TOMATO', 'LETTUCE', 'SPINACH', 'CARROT', 'SUNFLOWER', 'TOFU', 'CHICKPEAS', 'OIL']
kcal = {'TOMATO': 21,
'LETTUCE': 16,
'SPINACH': 40,
'CARROT': 41,
'SUNFLOWER': 585,
'TOFU': 120,
'CHICKPEAS': 164,
'OIL': 884}
protein = {'TOMATO': 0.85,
'LETTUCE': 1.62,
'SPINACH': 2.86,
'CARROT': 0.93,
'SUNFLOWER': 23.4,
'TOFU': 16,
'CHICKPEAS': 9,
'OIL': 0}
fat = {'TOMATO': 0.33,
'LETTUCE': 0.20,
'SPINACH': 0.39,
'CARROT': 0.24,
'SUNFLOWER': 48.7,
'TOFU': 5.0,
'CHICKPEAS': 2.6,
'OIL': 100.0}
carbs = {'TOMATO': 4.64,
'LETTUCE': 2.37,
'SPINACH': 3.63,
'CARROT': 9.58,
'SUNFLOWER': 15.0,
'TOFU': 3.0,
'CHICKPEAS': 27.0,
'OIL': 0.0}
sodium = {'TOMATO': 9.0,
'LETTUCE': 28.0,
'SPINACH': 65.0,
'CARROT': 69.0,
'SUNFLOWER': 3.80,
'TOFU': 120.0,
'CHICKPEAS': 78.0,
'OIL': 0.0}
cost = {'TOMATO': 1.0,
'LETTUCE': 0.75,
'SPINACH': 0.50,
'CARROT': 0.50,
'SUNFLOWER': 0.45,
'TOFU': 2.15,
'CHICKPEAS': 0.95,
'OIL': 2.00}
# Create the 'prob' variable to contain the problem data
prob = LpProblem("The Salad Problem", LpMinimize)
# A dictionary called 'ingredient_vars' is created to contain the referenced Variables
ingredient_vars = LpVariable.dicts("Ingr",Ingredients,0)
# The objective function is added to 'prob' first
prob += lpSum([kcal[i]*ingredient_vars[i] for i in Ingredients]), "Total kCal of Ingredients per salad"
# The constraints are added to 'prob'
prob += lpSum([protein[i] * ingredient_vars[i] for i in Ingredients]) >= 15.0, "ProteinRequirement"
prob += 8.0 >= lpSum([fat[i] * ingredient_vars[i] for i in Ingredients]) >= 2.0, "FatRequirement"
prob += lpSum([carbs[i] * ingredient_vars[i] for i in Ingredients]) >= 4.0, "CarbRequirement"
prob += lpSum([sodium[i] * ingredient_vars[i] for i in Ingredients]) <= 200.0, "SodiumRequirement"
prob += lpSum(prob.variables()[2].varValue + prob.variables()[4].varValue) / lpSum([prob.variables()[i].varValue for i in range(8)]) >= 0.40, "GreensRequirement"
prob.solve()
# The status of the solution is printed to the screen
print("Status:", LpStatus[prob.status])
# Each of the variables is printed with it's resolved optimum value
for v in prob.variables():
print(v.name, "=", v.varValue)
# The optimised objective function value is printed to the screen
print("Total kCal of Ingredients per salad = ", value(prob.objective))
这是给我带来问题的约束:
prob += lpSum(prob.variables()[2].varValue + prob.variables()[4].varValue) / lpSum([prob.variables()[i].varValue for i in range(8)]) >= 0.40, "GreensRequirement"
这会导致在 NoneType 上使用 + 运算符时出错,因为变量还没有值。我只是不确定如何设置这种约束。我已经查看了关于此的 PuLP 文档,但我还没有找到解决问题的运气。
一些评论
- 正如您自己所说,您不能建立基于某些尚未优化的决策变量的值的约束!
- 无法使用
.varValue
约束
- 无法使用
- 我一般不明白
prob.variables()[2]
的概念,而不是像以前那样访问ingredient_vars[i]
(但因为我不知道prob.variables
[=82= 到底是什么] 我会忽略这个) - 如果您替换
.varValue
访问并直接使用变量,您将构建一个带有 分部 的约束,它是非线性的,纸浆不会接受这个- 即使纸浆接受这个,求解器也无法解决这个问题(LP-求解器)
做什么
我们需要:
- 建立一个线性约束来表示你的比率约束
理论
我们使用 lpsolve here 文档中描述的重新表述。摘录:
在实践中
备注:由于我不熟悉变量的prob.variables()[2]
类访问并且懒得查看文档,我在这里猜测绿色蔬菜是:生菜+菠菜。如果我误解了这个,请随时更改它!
约束看起来是硬编码的(不太好,但对于您的示例来说可能没问题),如下所示:
prob += (0 - 0.4) * ingredient_vars['TOMATO'] + (1 - 0.4) * ingredient_vars['LETTUCE'] + \
(1 - 0.4) * ingredient_vars['SPINACH'] + (0 - 0.4) * ingredient_vars['CARROT'] + \
(0 - 0.4) * ingredient_vars['SUNFLOWER'] + (0 - 0.4) * ingredient_vars['TOFU'] + \
(0 - 0.4) * ingredient_vars['CHICKPEAS'] + (0 - 0.4) * ingredient_vars['OIL'] >= 0.0, 'GreensRequirement'
这是从 lpsolve 文档的公式直接翻译而来的。检查我们必须做出的假设!
示例输出
两个完全使用您的代码和替换约束的示例(考虑到我对绿色成分的假设):
比率为 0.4(如上面的代码所述)
('Status:', 'Optimal')
('Ingr_CARROT', '=', 0.0)
('Ingr_CHICKPEAS', '=', 0.0)
('Ingr_LETTUCE', '=', 0.58548009)
('Ingr_OIL', '=', 0.0)
('Ingr_SPINACH', '=', 0.0)
('Ingr_SUNFLOWER', '=', 0.0)
('Ingr_TOFU', '=', 0.87822014)
('Ingr_TOMATO', '=', 0.0)
('Total kCal of Ingredients per salad = ', 114.75409824)
比率为 0.9
('Status:', 'Optimal')
('Ingr_CARROT', '=', 0.0)
('Ingr_CHICKPEAS', '=', 0.0)
('Ingr_LETTUCE', '=', 4.4146501)
('Ingr_OIL', '=', 0.0)
('Ingr_SPINACH', '=', 0.0)
('Ingr_SUNFLOWER', '=', 0.0)
('Ingr_TOFU', '=', 0.49051668)
('Ingr_TOMATO', '=', 0.0)
('Total kCal of Ingredients per salad = ', 129.4964032)