解决经典饮食模型

解决经典的饮食模型

diet

#解决经典饮食模型,展示如何为现有模型添加约束
import gurobipy as gp
from gurobipy import GRB
import dietmodel
categories , minNutrition , maxNutrition = gp.multidict ({
'calories ': [1800 , 2200] ,
'protein ': [91 , GRB.INFINITY ] ,#INFINITY为无穷
'fat ': [0 , 65] ,
'sodium ': [0 , 1779]})
foods , cost = gp.multidict ({
'hamburger ': 2.49 ,
'chicken ': 2.89 ,
'hot dog ': 1.50 ,
'fries ': 1.89 ,
'macaroni ': 2.09 ,
'pizza ': 1.99 ,
'salad ': 2.49 ,
'milk ': 0.89 ,
'ice cream ': 1.59})
#Nutrition values for the foods
nutritionValues = {
('hamburger ', 'calories '): 410 ,
('hamburger ', 'protein '): 24 ,
('hamburger ', 'fat '): 26 ,
('hamburger ', 'sodium '): 730 ,
('chicken ', 'calories '): 420 ,
('chicken ', 'protein '): 32 ,
('chicken ', 'fat '): 10 ,
('chicken ', 'sodium '): 1190 ,
('hot dog ', 'calories '): 560 ,
('hot dog ', 'protein '): 20 ,
('hot dog ', 'fat '): 32 ,
('hot dog ', 'sodium '): 1800 ,
('fries ', 'calories '): 380 ,
('fries ', 'protein '): 4 ,
('fries ', 'fat '): 19 ,
('fries ', 'sodium '): 270 ,
('macaroni ', 'calories '): 320 ,
('macaroni ', 'protein '): 12 ,
('macaroni ', 'fat '): 10 ,
('macaroni ', 'sodium '): 930 ,
('pizza ', 'calories '): 320 ,
('pizza ', 'protein '): 15 ,
('pizza ', 'fat '): 12 ,
('pizza ', 'sodium '): 820 ,
('salad ', 'calories '): 320 ,
('salad ', 'protein '): 31 ,
('salad ', 'fat '): 12 ,
('salad ', 'sodium '): 1230 ,
('milk ', 'calories '): 100 ,
('milk ', 'protein '): 8 ,
('milk ', 'fat '): 2.5 ,
('milk ', 'sodium '): 125 ,
('ice cream ', 'calories '): 330 ,
('ice cream ', 'protein '): 8 ,
('ice cream ', 'fat '): 10 ,
('ice cream ', 'sodium '): 180}
# Model
m = gp . Model (" diet ")
# Create decision variables for the foods to buy 为购买的食物创建决策变量
buy = m . addVars ( foods , name ="buy")
# You could use Python looping constructs and m. addVar () to create these decision variables instead . The following would be equivalent(等价的)
# buy = {}
# for f in foods :
# buy[f] = m.addVar ( name =f)
# The objective is to minimize the costs    prod函数计算乘积
m.setObjective ( buy.prod ( cost ) , GRB.MINIMIZE )
# Using looping constructs , the preceding statement would be:使用循环结构,上面的语句将会是:
# m. setObjective (sum(buy[f]* cost [f] for f in foods ) , GRB. MINIMIZE )
# Nutrition constraints营养约束
m . addConstrs (( gp.quicksum ( nutritionValues [f , c ] * buy [ f ] for f in foods )
== [ minNutrition [ c ] , maxNutrition [ c ]]
for c in categories ) , "_")
# Using looping constructs , the preceding statement would be:
# for c in categories :
# m. addRange (sum( nutritionValues [f, c] * buy[f] for f in foods ) ,
# minNutrition [c] , maxNutrition [c] , c)
def printSolution ():
    if m.status == GRB.OPTIMAL :
        print ('\ nCost : %g' % m.ObjVal )#获取目标函数值
        print ('\ nBuy:')
        for f in foods:
            if buy [ f ].X > 0.0001:
                print ('%s %g' % (f , buy [ f ].X ))
    else:
        print ('No solution ')
# Solve
m.optimize ()
printSolution ()
print ('\ nAdding constraint : at most 6 servings of dairy ')
m.addConstr ( buy.sum ([ 'milk ', 'ice cream ']) <= 6 , " limit_dairy ")
# Solve 前面又增加了一个约束条件,所以要再次有下面的代码
m.optimize ()
printSolution ()

diet思路

问题一

内部函数不能直接调用,只可直接调用外部函数,dietmodel.solve ( categories , minNutrition , maxNutrition ,foods , cost , nutritionValues )可以在diet2调用,而dietmodel.printSolution()不可以

问题二

m.optimize导致的

问题三

优化完成后,可查询属性值,可查询X变量属性,以获得每个变量的解决方案值,0.0001是为了控制精度

diet2

import dietmodel
import gurobipy as gp
from gurobipy import GRB
categories , minNutrition , maxNutrition = gp.multidict ({
'calories ': [1800 , 2200] ,
'protein ': [91 , GRB.INFINITY ] ,
'fat ': [0 , 65] ,
'sodium ': [0 , 1779]})
foods , cost = gp.multidict ({
'hamburger ': 2.49 ,
'chicken ': 2.89 ,
'hot dog ': 1.50 ,
'fries ': 1.89 ,
'macaroni ': 2.09 ,
'pizza ': 1.99 ,
'salad ': 2.49 ,
'milk ': 0.89 ,
'ice cream ': 1.59})
# Nutrition values for the foods
nutritionValues = {
('hamburger ', 'calories '): 410 ,
('hamburger ', 'protein '): 24 ,
('hamburger ', 'fat '): 26 ,
('hamburger ', 'sodium '): 730 ,
('chicken ', 'calories '): 420 ,
('chicken ', 'protein '): 32 ,
('chicken ', 'fat '): 10 ,
('chicken ', 'sodium '): 1190 ,
('hot dog ', 'calories '): 560 ,
('hot dog ', 'protein '): 20 ,
('hot dog ', 'fat '): 32 ,
('hot dog ', 'sodium '): 1800 ,
('fries ', 'calories '): 380 ,
('fries ', 'protein '): 4 ,
('fries ', 'fat '): 19 ,
('fries ', 'sodium '): 270 ,
('macaroni ', 'calories '): 320 ,
('macaroni ', 'protein '): 12 ,
('macaroni ', 'fat '): 10 ,
('macaroni ', 'sodium '): 930 ,
('pizza ', 'calories '): 320 ,
('pizza ', 'protein '): 15 ,
('pizza ', 'fat '): 12 ,
('pizza ', 'sodium '): 820 ,
('salad ', 'calories '): 320 ,
('salad ', 'protein '): 31 ,
('salad ', 'fat '): 12 ,
('salad ', 'sodium '): 1230 ,
('milk ', 'calories '): 100 ,
('milk ', 'protein '): 8 ,
('milk ', 'fat '): 2.5 ,
('milk ', 'sodium '): 125 ,
('ice cream ', 'calories '): 330 ,
('ice cream ', 'protein '): 8 ,
('ice cream ', 'fat '): 10 ,
('ice cream ', 'sodium '): 180}
dietmodel.solve ( categories , minNutrition , maxNutrition ,
foods , cost , nutritionValues )

diet2思路

dietmodel

一个制定和求解模型的函数,但它不包括模型数据,数据由调用程序调用。

# Solve the classic diet model.  This file implements a function that formulates and solves the model,
#  but it contains no model data.  The data is
# passed in by the calling program.  Run example 'diet2.py',
# 'diet3.py', or 'diet4.py' to invoke this function.
#解决经典饮食模式,这个文件实现了一个制定和求解模型的函数,但它不包含模型数据,数据由调用程序传入。运行示例'diet2.py'、'diet3.py'、'diet4.py'来调用这个函数
import gurobipy as gp
from gurobipy import GRB
def solve(categories, minNutrition, maxNutrition, foods, cost,
          nutritionValues):
    # Model
    m = gp.Model("diet")
    # Create decision variables for the foods to buy
    buy = m.addVars(foods, name="buy")
    # The objective is to minimize the costs
    m.setObjective(buy.prod(cost), GRB.MINIMIZE)
    # Nutrition constraints
    m.addConstrs((gp.quicksum(nutritionValues[f, c] * buy[f] for f in foods) ==
                 [minNutrition[c], maxNutrition[c]] for c in categories), "_")

    def printSolution():
        if m.status == GRB.OPTIMAL:
            print('\nCost: %g' % m.ObjVal)
            print('\nBuy:')
            for f in foods:
                if buy[f].X > 0.0001:
                    print('%s %g' % (f, buy[f].X))
        else:
            print('No solution')

    # Solve
    m.optimize()
    printSolution()
    print('\nAdding constraint: at most 6 servings of dairy')
    m.addConstr(buy.sum(['milk', 'ice cream']) <= 6, "limit_dairy")
    # Solve
    m.optimize()
    printSolution()

dietmodel思路:

做到花最少的钱去买食物使营养约束在指定范围内

 一些知识储备:

multidict函数

允许在一条语句中初始化一个或多个字典,该函数以字典为参数,其中与每个键相关联的值是一个长度为n的列表。该函数将这些列表拆分成单个条目,创建n个独立的字典。函数返回一个列表。该列表第一项是共享键值列表,然后是n个单独的字典。

import gurobipy as gp
from gurobipy import GRB
names, lower, upper = gp.multidict({ 'x': [0, 1], 'y': [1, 2], 'z': [0, 3] })
print(names)
['x', 'y', 'z']
print(lower)
{'x':0, 'y':1, 'z':0}
print(upper)
{'x':1, 'y':2, 'z':3}

列表理解和生成器表达式

是重要的Python特性,让以简洁的方式进行隐式枚举。

列表理解

建立一个包含1到5的数学平方的列表

gurobi> [x*x for x in [1, 2, 3, 4, 5]]
[1, 4, 9, 16, 25]

可先用列表理解来构建列表,然后将列表传递给sum。不过生成器表达式更简单、更高效。

生成器表达式

gurobi> sum(x*x for x in [1, 2, 3, 4, 5])

只要方法接收Interable参数(可以被遍历的东西),就可以使用生成器表达式。例如,大多数接受list参数(最常见的Iterable类型)的Python方法也会接受生成器表达式。

生成器表达式在我们的Python示例中被广泛使用,主要是在使用addConstrs方法一次创建多个约束时。

注意:有一个Python例程可以创建连续的整数列表:range。上面的代码通常可写成:

gurobi> sum(x*x for x in range(1,6))

列表理解和生成器表达式都可以包含多个for子句和一个或多个if子句

[(x,y) for x in range(4) for y in range(4) if x < y]
[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]

构建了一个包含所有x,y对的元组列表,其中x和y均小于4,且x小于y。

元组列表类(tupledict)

tuplelist对象上的select方法来检索与特定字段中的一个或多个指定值相匹配的所有图元

‘*’表示可以接受任何可以取到的值

gurobi> print(l.select(1, '*'))
2个元组(前一项为1的只有2个),每个元组2个值(列)
(1,2)
(1,3)
gurobi> print(l.select('*', 3))
2个元组(第二项为3的只有2个),每个元组2个值(两列)
( 1 , 3 )
( 2 , 3 )
gurobi> print(l.select('*', [2, 4]))
2个元组(第二项为2或4的只有两项),每个元组2个值
( 1 , 2 )
( 2 , 4 )
gurobi> print(l.select('*', '*'))
4个元组()第一二项都可以接受任何可以取到的值,每个元组2个值
( 1 , 2 )
( 1 , 3 )
( 2 , 3 )
( 2 , 4 )

使用列表理解也可以获得类似的结构。例如:

gurobi> print(l.select(1, '*'))
2个元组(前一项为1的只有2个),每个元组2个值(列)
(1,2)
(1,3)
gurobi>l=tuplelist([(1,2),(1,3),(2,3),(2,4)])
print([(x,y) for x,y in l if x==1])

问题在于,后一条语句会考虑列表l中的每个成员,这对大型列表来说效率很低。select方法建立了内部数据结构,使这些选择变得相当高效。

元组类(tupledict)

用tupledict对象上的sum和prod方法构建线性表达式

轻松简洁

gurobi构建线性表达式,变量的系数为1时使用sum方法,变量的系数不为1时使用prod方法(变量和系数相乘后累加)。

使用sum和prod方法的代码如下:

import gurobipy as grb
model = grb.Model()
# 定义变量的下标
tl = [(1, 1), (1, 2), (1, 3),
      (2, 1), (2, 2), (2, 3),
      (3, 1), (3, 2), (3, 3)]
vars = model.addVars(tl, name = "x")
model.update()
# 创建一个系数矩阵,用tuplelist格式存储,键(key)和vars一样
c1 = [(1, 1), (1, 2), (1, 3)]
coeff = grb.tupledict(c1)
coeff[(1, 1)] = 1
coeff[(1, 2)] = 0.3
coeff[(1, 3)] = 0.4
# 变量系数是1的时候用sum方法
print(vars.sum(1, '*'))
# 输出
# <gurobi.LinExpr: x[1,1] + x[1,2] + x[1,3]>
# prod方法:变量系数不是1,变量和系数相乘后累加
print(vars.prod(coeff, 1, '*'))
# 输出
# <gurobi.LinExpr: x[1,1] + 0.3 x[1,2] + 0.4 x[1,3]>

addVars方法为输入参数中的每个元组添加一个Gurobi决策变量到模型中,并以tupledict的形式返回结果,将创建变量d(1,2)、d(1,3)、d(2,3)、d(2,4)。

sum方法
import gurobipy as gp
from gurobipy import GRB
l = list([(1, 2), (1, 3), (2, 3), (2, 4)])
model=gp.Model()
d = model.addVars(l, name="d")
model.update()
#构建线性表达式
print((sum(d.select(1,'*'))))
#或上式可为print(d.sum('*',3))代替
#d[1,2] + d[1,3]
#Python sum语句会创建一个线性表达式来捕捉这些变量的总和。得到的表达式为d(1,2)+d(1,3)
prod方法

系数通过dict参数提供,它们使用与tupledict相同的元组进行索引。

import gurobipy as grb
model = grb.Model()
# 定义变量的下标
tl = [(1, 1), (1, 2), (1, 3),
      (2, 1), (2, 2), (2, 3),
      (3, 1), (3, 2), (3, 3)]
vars = model.addVars(tl, name = "x")
model.update()
# 创建一个系数矩阵,用tuplelist格式存储,键(key)和vars一样
c1 = [(1, 1), (1, 2), (1, 3)]
coeff = grb.tupledict(c1)
coeff[(1, 1)] = 1
coeff[(1, 2)] = 0.3
coeff[(1, 3)] = 0.4
# prod方法:变量系数不是1,变量和系数相乘后累加
print(vars.prod(coeff, 1, '*'))
# 输出
# <gurobi.LinExpr: x[1,1] + 0.3 x[1,2] + 0.4 x[1,3]>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值