用线性规划解决配方问题
Posted bkzy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用线性规划解决配方问题相关的知识,希望对你有一定的参考价值。
练习使用线性规划算法包解决猫粮配方的问题。
问题
某猫粮生产商生产猫粮时使用鸡肉、牛肉、羊肉、大米、小麦麦麸和凝胶作为原材料。这些原材料的营养成分的含量和价格如表所示。
物品 | 蛋白质 | 脂肪 | 纤维 | 盐 | 单价 |
---|---|---|---|---|---|
鸡肉 Chicken | 0.1 | 0.08 | 0.001 | 0.002 | 0.013 |
牛肉Beef | 0.2 | 0.1 | 0.005 | 0.005 | 0.008 |
羊肉Mutton | 0.15 | 0.11 | 0.003 | 0.007 | 0.01 |
大米Rice | 0 | 0.01 | 0.1 | 0.002 | 0.002 |
麦麸Wheat bran | 0.04 | 0.01 | 0.15 | 0.008 | 0.005 |
凝胶Gel | 0 | 0 | 0 | 0 | 0.001 |
成品要求(克/百克) | >=8 | >=6 | <=2 | <=0.4 |
- 成分单位: g/g
- 单价单位: 美元/g
- 成品中成分单位: g/100g
问:每100克产品中应如何配比原材料,才能在满足产品营养成分的情况下做到成本最小化?
下面练习使用线性规划pulp算法包解决此问题。
安装算法包
pulp源码地址:https://github.com/coin-or/pulp
pulp文档地址:https://coin-or.github.io/pulp/
pip install pulp
简化解题示例
- 假设只想用两种原料制作猫粮:鸡肉和牛肉
# 导入算法包
import pulp as pl
创建问题
- 使用
LpProblem
函数创建问题.LpProblem
有两个参数:- 第一个参数为问题的名称,可以是任意字符串
- 第二个参数为 LpMinimize(默认值) 或者 LpMaximize
# 创建问题
prob=pl.LpProblem("猫粮配方问题",pl.LpMinimize)
创建变量
- 使用
LpVariable
函数创建变量。LpVariable
有四个参数:name
:输出时使用的变量名,任意字符串lowBound
:变量下限,默认None
upBound
:变量上限,默认None
cat
:数据的属性,实质上是数据的类型(离散或者连续),可选:“pl.LpInterger”、“pl.LpBinary"或者"pl.LpContinuous”(默认)
# 创建变量
# 鸡肉百分比,最小值为0,最大值不限
x1=pl.LpVariable("鸡肉百分比",0,None)
# 牛肉百分比,最小值为0,最大值不限
x2=pl.LpVariable("牛肉百分比",0,None)
建立目标函数
- 目标函数结尾有一个逗号
,
,其后是一个简短的字符串来解释这个目标函数是干什么的
# 建立目标函数
prob+=0.013 * x1 + 0.008 * x2, "每罐产品的总成本"
建立约束
- 定义变量的时候已经包含了任意的非负约束
- 这再次使用“+=”运算符完成,因为我们正在向prob变量添加更多数据 。在此之后按逻辑输入约束,在约束方程的末尾加上一个逗号,并简要描述该约束的原因
# 5个约束条件需要输入
prob += x1 + x2 ==100,"每罐猫粮100克"
prob += 0.100 * x1 + 0.200 * x2 >= 8.0,"每100克猫粮的蛋白质含量必须不小于8克"
prob += 0.080 * x1 + 0.100 * x2 >= 6.0,"每100克猫粮的脂肪含量必须不小于6克"
prob += 0.001 * x1 + 0.005 * x2 <= 2.0,"每100克猫粮的纤维含量必须不大于2克"
prob += 0.002 * x1 + 0.005 * x2 <= 0.4,"每100克猫粮的食盐含量必须不大于0.4克"
解决问题
- 使用
solve()
函数计算问题 solve()
可以输入求解器参数。solve()
的返回值为解决问题的状态,0: ‘Not Solved[未解决]’, 1: ‘Optimal[最佳]’, -1: ‘Infeasible[不可行]’, -2: ‘Unbounded[无界]’, -3: ‘Undefined[未定义]’- 状态值保存在
.status
变量中 - 通过LpStatus查看状态的文字描述
- 定义的
x1
、x2
等变量的值保存在.variables()
中
# 解决问题
prob.solve()
print("状态:",pl.LpStatus[prob.status])
"""
打印输出为:
状态: Optimal
"""
# 输出变量值
for v in prob.variables():
print(v.name,"=",v.varValue)
"""
打印输出为:
牛肉百分比 = 66.666667
鸡肉百分比 = 33.333333
"""
# 输出结果
print("每百克成品的成本价 = ",pl.value(prob.objective))
"""
打印输出为:
每百克成品的成本价 = 0.966666665
"""
完整的程序
# 导入算法包
import pulp as pl
# 创建问题
prob=pl.LpProblem("猫粮配方问题",pl.LpMinimize)
# 创建变量
# 鸡肉百分比,最小值为0,最大值不限
x1=pl.LpVariable("鸡肉百分比",0,None)
# 牛肉百分比,最小值为0,最大值不限
x2=pl.LpVariable("牛肉百分比",0,None)
# 建立目标函数
prob+=0.013 * x1 + 0.008 * x2, "每罐产品的总成本"
# 建立约束
# 5个约束条件需要输入
prob += x1 + x2 ==100,"每罐猫粮100克"
prob += 0.100 * x1 + 0.200 * x2 >= 8.0,"每100克猫粮的蛋白质含量必须不小于8克"
prob += 0.080 * x1 + 0.100 * x2 >= 6.0,"每100克猫粮的脂肪含量必须不小于6克"
prob += 0.001 * x1 + 0.005 * x2 <= 2.0,"每100克猫粮的纤维含量必须不大于2克"
prob += 0.002 * x1 + 0.005 * x2 <= 0.4,"每100克猫粮的食盐含量必须不大于0.4克"
# 解决问题
prob.solve()
print("状态:",pl.LpStatus[prob.status])
# 输出变量值
for v in prob.variables():
print(v.name,"=",v.varValue)
# 输出结果
print("每百克成品的成本价 = ",pl.value(prob.objective))
完整解题程序示例
import pulp as pl
# 创建原料表
Ingredients = ["CHICKEN", "BEEF", "MUTTON", "RICE", "WHEAT", "GEL"]
# 原材料成本单价(美元/克)
costs =
"CHICKEN": 0.013,
"BEEF": 0.008,
"MUTTON": 0.010,
"RICE": 0.002,
"WHEAT": 0.005,
"GEL": 0.001,
# 原材料中蛋白质的含量(g/g)
proteinPercent =
"CHICKEN": 0.100,
"BEEF": 0.200,
"MUTTON": 0.150,
"RICE": 0.000,
"WHEAT": 0.040,
"GEL": 0.000,
# 原材料中脂肪的含量(g/g)
fatPercent =
"CHICKEN": 0.080,
"BEEF": 0.100,
"MUTTON": 0.110,
"RICE": 0.010,
"WHEAT": 0.010,
"GEL": 0.000,
# 原材料中纤维的含量(g/g)
fibrePercent =
"CHICKEN": 0.001,
"BEEF": 0.005,
"MUTTON": 0.003,
"RICE": 0.100,
"WHEAT": 0.150,
"GEL": 0.000,
# 原材料中盐分的含量(g/g)
saltPercent =
"CHICKEN": 0.002,
"BEEF": 0.005,
"MUTTON": 0.007,
"RICE": 0.002,
"WHEAT": 0.008,
"GEL": 0.000,
创建问题
# 创建问题
prob = pl.LpProblem("猫粮配方问题",pl.LpMinimize)
创建变量
- 使用
LpVariable.dicts()
集中创建变量,参数:name
:字符串,变量名称的前缀indexs
:字符串list,变量(一般是原材料名)的名称列表lowBound
:变量下限,默认None
upBound
:变量上限,默认None
cat
:数据的属性,实质上是数据的类型(离散或者连续),可选:“pl.LpInterger”、“pl.LpBinary"或者"pl.LpContinuous”(默认)
ingredient_vars = pl.LpVariable.dicts("Ingr",Ingredients,0)
print(ingredient_vars)
打印出来的变量结果为:
'CHICKEN': Ingr_CHICKEN, 'BEEF': Ingr_BEEF, 'MUTTON': Ingr_MUTTON, 'RICE': Ingr_RICE, 'WHEAT': Ingr_WHEAT, 'GEL': Ingr_GEL
创建目标函数
#创建目标函数
prob += pl.lpSum([costs[i]*ingredient_vars[i] for i in Ingredients])
创建约束
#添加约束条件
# 所有参数的和为100
prob += pl.lpSum([ingredient_vars[i] for i in Ingredients]) == 100
# 每100克猫粮的蛋白质含量必须不小于8克
prob += pl.lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 8.0
# 每100克猫粮的脂肪含量必须不小于6克
prob += pl.lpSum([fatPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 6.0
# 每100克猫粮的纤维含量必须不大于2克
prob += pl.lpSum([fibrePercent[i] * ingredient_vars[i] for i in Ingredients]) <= 2.0
# 每100克猫粮的盐分含量必须不大于0.4克
prob += pl.lpSum([saltPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 0.4
解决问题
# 解决问题
prob.solve()
print("状态:",pl.LpStatus[prob.status])
"""
打印输出为:
状态: Optimal
"""
# 输出变量值
for i in Ingredients:
print(ingredient_vars[i],"=",ingredient_vars[i].value())
"""
打印输出为:
Ingr_CHICKEN = 0.0
Ingr_BEEF = 60.0
Ingr_MUTTON = 0.0
Ingr_RICE = 0.0
Ingr_WHEAT = 0.0
Ingr_GEL = 40.0
"""
# 输出结果
print("每百克成品的成本价 = ",pl.value(prob.objective))
"""
打印输出为:
每百克成品的成本价 = 0.52
"""
完整的程序
# 导入包
import pulp as pl
# 创建原料表
Ingredients = ["CHICKEN", "BEEF", "MUTTON", "RICE", "WHEAT", "GEL"]
# 原材料成本单价(美元/克)
costs =
"CHICKEN": 0.013,
"BEEF": 0.008,
"MUTTON": 0.010,
"RICE": 0.002,
"WHEAT": 0.005,
"GEL": 0.001,
# 原材料中蛋白质的含量(g/g)
proteinPercent =
"CHICKEN": 0.100,
"BEEF": 0.200,
"MUTTON": 0.150,
"RICE": 0.000,
"WHEAT": 0.040,
"GEL": 0.000,
# 原材料中脂肪的含量(g/g)
fatPercent =
"CHICKEN": 0.080,
"BEEF": 0.100,
"MUTTON": 0.110,
"RICE": 0.010,
"WHEAT": 0.010,
"GEL": 0.000,
# 原材料中纤维的含量(g/g)
fibrePercent =
"CHICKEN": 0.001,
"BEEF": 0.005,
"MUTTON": 0.003,
"RICE": 0.100,
"WHEAT": 0.150,
"GEL": 0.000,
# 原材料中盐分的含量(g/g)
saltPercent =
"CHICKEN": 0.002,
"BEEF": 0.005,
"MUTTON": 0.007,
"RICE": 0.002,
"WHEAT": 0.008,
"GEL": 0.000,
# 创建问题
prob = pl.LpProblem("猫粮配方问题",pl.LpMinimize)
# 创建变量
ingredient_vars = pl.LpVariable.dicts("Ingr",Ingredients,0)
#创建目标函数
prob += pl.lpSum([costs[i]*ingredient_vars[i] for i in Ingredients])
#添加约束条件
# 所有参数的和为100
prob += pl.lpSum([ingredient_vars[i] for i in Ingredients]) == 100
# 每100克猫粮的蛋白质含量必须不小于8克
prob += pl.lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 8.0
# 每100克猫粮的脂肪含量必须不小于6克
prob += pl.lpSum([fatPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 6.0
# 每100克猫粮的纤维含量必须不大于2克
prob += pl.lpSum([fibrePercent[i] * ingredient_vars[i] for i in Ingredients]) <= 2.0
# 每100克猫粮的盐分含量必须不大于0.4克
prob += pl.lpSum([saltPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 0.4
# 解决问题
prob.solve()
print("状态:",pl.LpStatus[prob.status])
# 输出变量值
for i in Ingredients:
print(ingredient_vars[i],"=",ingredient_vars[i].value())
# 输出结果
print("每百克成品的成本价 = ",pl.value(prob.objective))
参考资料
https://coin-or.github.io/pulp/CaseStudies/a_blending_problem.html
以上是关于用线性规划解决配方问题的主要内容,如果未能解决你的问题,请参考以下文章