用线性规划解决配方问题

Posted bkzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用线性规划解决配方问题相关的知识,希望对你有一定的参考价值。

练习使用线性规划算法包解决猫粮配方的问题。

问题

某猫粮生产商生产猫粮时使用鸡肉、牛肉、羊肉、大米、小麦麦麸和凝胶作为原材料。这些原材料的营养成分的含量和价格如表所示。

物品蛋白质脂肪纤维单价
鸡肉 Chicken0.10.080.0010.0020.013
牛肉Beef0.20.10.0050.0050.008
羊肉Mutton0.150.110.0030.0070.01
大米Rice00.010.10.0020.002
麦麸Wheat bran0.040.010.150.0080.005
凝胶Gel00000.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有两个参数:
    1. 第一个参数为问题的名称,可以是任意字符串
    2. 第二个参数为 LpMinimize(默认值) 或者 LpMaximize
# 创建问题
prob=pl.LpProblem("猫粮配方问题",pl.LpMinimize)

创建变量

  • 使用LpVariable函数创建变量。LpVariable有四个参数:
    1. name:输出时使用的变量名,任意字符串
    2. lowBound:变量下限,默认None
    3. upBound:变量上限,默认None
    4. 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查看状态的文字描述
  • 定义的x1x2等变量的值保存在.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()集中创建变量,参数:
    1. name:字符串,变量名称的前缀
    2. indexs:字符串list,变量(一般是原材料名)的名称列表
    3. lowBound:变量下限,默认None
    4. upBound:变量上限,默认None
    5. 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

以上是关于用线性规划解决配方问题的主要内容,如果未能解决你的问题,请参考以下文章

用线性规划解决配方问题

用线性规划解决配方问题

如何处理数据库中的配方成分

编程范式--对象范式,这不是原来的配方了

用机器学习:分析了 600 多种烘焙配方,开发出新品

查找已安装的石墨版本