几种常见的线性规划模型

钢管切割问题

题目要求

某单位需要加工制作100套钢架,每套钢架长为2.9m,2.1m和1m的圆钢各一根。已知原料长为6.9m,问如何切割原材料可以使得使用的原材料最少

分析

考虑最简单的切割方法,准备100根原材料,每根原材料上面都截取2.9m,2.1m和1m的圆钢各一根,一共需要100根原材料,每根原材料剩下的钢管为0.9m,此时,我们对一根钢管可截取的圆钢数目进行枚举,写出所有可行的切割方案(同时要求剩余长度不超过1m),使用Python语言进行枚举,如下表所示。

2.9m 数量2.1m 数量1m 数量合计长度料头长度
1046.90.0
2016.80.1
0306.30.6
0226.20.7
0146.10.8
0066.00.9
1116.00.9

可以得出,一共有七种切割方案

具体枚举计算代码

# 钢架的长度
lengths = [2.9, 2.1, 1.0]

# 原材料长度
material_length = 6.9

# 枚举所有的切割方式
cutting_patterns = []
for i in range(0, int(material_length / lengths[0]) + 1):
    for j in range(0, int(material_length / lengths[1]) + 1):
        for k in range(0, int(material_length / lengths[2]) + 1):
            total_length = i * lengths[0] + j * lengths[1] + k * lengths[2]
            leftover = material_length - total_length
            if total_length <= material_length and leftover <1:
                cutting_patterns.append((i, j, k, total_length, leftover))

# 按照料头长度从小到大排序
cutting_patterns.sort(key=lambda x: x[4])

# 打印结果表格
print("2.9m 数量\t2.1m 数量\t1m 数量\t合计长度\t料头长度")
for pattern in cutting_patterns:
    i, j, k, total_length, leftover = pattern
    print(f"{i}\t\t{j}\t\t{k}\t\t{total_length:.1f}\t\t{leftover:.1f}")

模型的建立

为了求出使用原材料的最小值,假设按照方案A-G切割的原材料数量分别为 x i ( i = 1 , 2 , 3 … , 7 ) x_i(i=1,2,3\dots,7) xi(i=1,2,3,7) ,根据上表的数据我们可以建立以下线性整数规划模型,目标函数
min ⁡ ∑ i = 1 7 x i \min\sum_{i=1}^{7}x_{i} mini=17xi
约束条件

  1. 得到2.9m的钢管的数目大于等于100根
    x 1 + 2 x 2 + x 7 ≥ 100 x_1+2x_2+x_7\geq100 x1+2x2+x7100

  2. 得到2.1m的钢管数目大于等于100
    3 x 3 + 3 x 4 + x 5 + x 7 ≥ 100 3x_3+3x_4+x_5+x_7\geq100 3x3+3x4+x5+x7100

  3. 得到1m的钢管数目大于等于100
    4 x 1 + x 2 + 2 x 4 + 4 x 5 + 6 x 6 + x 7 ≥ 100 4x_1+x_2+2x_4+4x_5+6x_6+x_7\geq100 4x1+x2+2x4+4x5+6x6+x7100

  4. x i x_{i} xi为整非负整数
    x i 是非负整数 ( i = 1 , 2 , ⋯   , 7 ) x_i\text{是非负整数}(i=1,2,\cdots,7) xi是非负整数(i=1,2,,7)

建立完整的优化模型
min ⁡ ∑ i = 1 7 x i s . t . { x 1 + 2 x 2 + x 7 ≥ 100 3 x 3 + 2 x 4 + x 5 + x 7 ≥ 100 4 x 1 + x 2 + 2 x 4 + 4 x 5 + 6 x 6 + x 7 ≥ 100 x i 是非负整数 ( i = 1 , 2 , ⋯   , 7 ) \min\sum_{i=1}^{7}x_{i}\\ \left.s.t.\quad\left\{\begin{array}{l}x_1+2x_2+x_7\geq100\\3x_3+2x_4+x_5+x_7\geq100\\4x_1+x_2+2x_4+4x_5+6x_6+x_7\geq100\\x_i\text{是非负整数}(i=1,2, \cdots,7)\end{array}\right.\right. mini=17xis.t. x1+2x2+x71003x3+2x4+x5+x71004x1+x2+2x4+4x5+6x6+x7100xi是非负整数(i=1,2,,7)

模型的求解

这里使用Python语言的pulp库来解决这个线性规划问题,目标是最小化资源的使用量。具体的步骤

  1. 定义线性规划问题

    my_tube = pulp.LpProblem('tube', LpMinimize)
    

    作用: 这行代码定义了一个名为 my_tube 的线性规划问题,其中 'tube' 是问题的名称,LpMinimize 表示这是一个最小化问题。也就是说,目标是找到变量的值,使得目标函数的值最小。

  2. 定义决策变量

    x1 = LpVariable('x1', lowBound=0, cat=LpInteger)
    x2 = LpVariable('x2', lowBound=0, cat=LpInteger)
    x3 = LpVariable('x3', lowBound=0, cat=LpInteger)
    x4 = LpVariable('x4', lowBound=0, cat=LpInteger)
    x5 = LpVariable('x5', lowBound=0, cat=LpInteger)
    x6 = LpVariable('x6', lowBound=0, cat=LpInteger)
    x7 = LpVariable('x7', lowBound=0, cat=LpInteger)
    

    这些代码定义了七个决策变量 x1x7,每个变量代表使用某种切割方案的次数。

    lowBound=0: 表示这些变量的下界是0,意味着这些变量不能取负值。

    cat=LpInteger: 表示这些变量必须是整数,因为你不能有部分次的切割方案。

  3. 定义目标函数

    my_tube += x1+x2+x3+x4+x5+x6+x7, 'obj'
    

    这一行定义了目标函数,即将所有变量的和最小化。

    目标函数是 x1 + x2 + x3 + x4 + x5 + x6 + x7,表示使用的原材料的总数。这个表达式的最小值就是我们希望求解的结果。

  4. 定义约束条件

    my_tube += x1 + 2*x2 + x7 >= 100, 'c1'
    my_tube += 3*x3+2*x4+x5+x7 >= 100, 'c2'
    my_tube += 4*x1+x2+2*x4+4*x5+6*x6+x7 >= 100
    

    这些代码定义了线性规划问题的约束条件。

  5. 求解问题

    my_tube.solve()
    

    这行代码调用 PuLP 的求解器来解决这个线性规划问题。求解器会找到满足所有约束条件的变量值组合,使得目标函数最小化。

求解器运行后,你可以通过 value(x1)value(x2) 等方法来查看每个变量的最终值,从而知道使用了多少次每种切割方案,以及最小的原材料总数量是多少。

0-1背包问题

问题描述

0-1背包问题(0-1 Knapsack Problem)是经典的组合优化问题之一,常用于计算机科学、运筹学等领域。其核心是如何在给定容量的背包中选择物品,使得背包内物品的总价值最大化。0-1背包问题中的“0-1”表示每个物品只能选择一次,要么放入背包(选择1),要么不放入背包(选择0)。

假设有一个背包,容量为 𝐶(即背包能容纳的最大重量),同时有 𝑛个物品,每个物品 𝑖有两个属性:

  • 重量 w i w_{i} wi
  • 价值 v i v_{i} vi

你需要决定是否将每个物品放入背包中,从而使得在不超过背包容量的前提下,背包内物品的总价值最大化。

数学模型

x i x_{i} xi 为决策变量,表示第i个物品是否放入背包中:

x i = { 1 如果物品  i  被放入背包 0 如果物品  i  没有被放入背包 x_i=\begin{cases}1&\text{如果物品 }i\text{ 被放入背包}\\0&\text{如果物品 }i\text{ 没有被放入背包}&\end{cases} xi={10如果物品 i 被放入背包如果物品 i 没有被放入背包

目标最大化总价值

Maximize  ∑ i = 1 n v i ⋅ x i \text{Maximize }\sum_{i=1}^nv_i\cdot x_i Maximize i=1nvixi

同时,背包的总重量不能超过C:

Subject to ∑ i = 1 n w i ⋅ x i ≤ C \text{Subject to}\sum_{i=1}^nw_i\cdot x_i\leq C Subject toi=1nwixiC

具体例子

下面解决一个背包问题

  • 目标:最大化一个价值函数,这个价值函数是10个物品(‘x1’到‘x10’)的组合价值。
  • 约束条件:背包的总容量不能超过30个单位
物品价值重量
x15406
x22003
x31804
x43505
x5601
x61502
x72803
x84505
x93204
x101202

代码实现

from pulp import *
my_package = pulp.LpProblem('package', LpMaximize)
x1 = LpVariable('x1', cat=LpBinary)
x2 = LpVariable('x2', cat=LpBinary)
x3 = LpVariable('x3', cat=LpBinary)
x4 = LpVariable('x4', cat=LpBinary)
x5 = LpVariable('x5', cat=LpBinary)
x6 = LpVariable('x6', cat=LpBinary)
x7 = LpVariable('x7', cat=LpBinary)
x8 = LpVariable('x8', cat=LpBinary)
x9 = LpVariable('x9', cat=LpBinary)
x10 = LpVariable('x10', cat=LpBinary)
my_package += 540*x1+200*x2+180*x3+350*x4+60*x5+150*x6+280*x7+450*x8+320*x9+120*x10, 'obj'
my_package += 6*x1+3*x2+4*x3+5*x4+x5+2*x6+3*x7+5*x8+4*x9+2*x10<=30, 'c1'
my_package.solve()
value(my_package.objective)
for v in my_package.variables():
    print(v.name,'=',v.varValue)

指派问题

问题背景

五名候选人组成4*100米混合泳接力

五个人的成绩

蝶泳仰泳蛙泳自由泳
66.875.68758.6
57.26666.453
7867.884.659.4
7074.269.657.2
67.47183.862.4

模型的建立

  1. 确定目标函数

    根据题意,制定一个选择方案,使得最终的成绩最好(即所用时间最短)。建立目标函数:
    min ⁡ ∑ j = 1 4 ∑ i = 1 5 t i j x i j \min\sum_{j=1}^{4}\sum_{i=1}^{5}t_{ij}x_{ij} minj=14i=15tijxij

    其中 i i i表示所选择的队员, j j j表示所选择的泳姿, x i j x_{ij} xij为决策变量,当 x i j = 1 x_{ij}=1 xij=1时,队员 i i i参加第 j j j种泳姿,否则不参加, t i j t_{ij} tij表示队员 i i i参加第 j j j种泳姿的耗时。

  2. 约束条件

    1. 每人只能选一种泳姿
      ∑ j = 1 4 x j j ≤ 1 , i = 1 , 2 , 3 , 4 , 5 \sum_{j=1}^{4}x_{jj}\leq1,i=1,2,3,4,5 j=14xjj1,i=1,2,3,4,5

    2. 每种泳姿都有一人选
      ∑ i = 1 5 x i j = 1 , j = 1 , 2 , 3 , 4 \sum_{i=1}^{5}x_{ij}=1,j=1,2,3,4 i=15xij=1,j=1,2,3,4

    3. x i j x_{ij} xij的取值只能是0和1

      x i j ∈ { 0 , 1 } x_{ij}\in\{0,1\} xij{0,1}

  3. 优化模型的整合体现

min ⁡ ∑ j = 1 4 ∑ i = 1 5 t i j x i j s t . { ∑ j = 1 4 x j j ≤ 1 , i = 1 , 2 , 3 , 4 , 5 ∑ i = 1 5 x i j = 1 , j = 1 , 2 , 3 , 4 x i j ∈ { 0 , 1 } \min\sum_{j=1}^{4}\sum_{i=1}^{5}t_{ij}x_{ij} \\ st.\begin{cases} \sum_{j=1}^{4}x_{jj}\leq1,i=1,2,3,4,5\\ \sum_{i=1}^{5}x_{ij}=1,j=1,2,3,4 \\x_{ij}\in\{0,1\} \end{cases} minj=14i=15tijxijst. j=14xjj1,i=1,2,3,4,5i=15xij=1,j=1,2,3,4xij{0,1}

模型求解

解决方法,使用匈牙利算法,又称Kuhn-Munkres算法,来解决二分图的最小权匹配问题。具体而言,代码利用了SciPy库中的linear_sum_assignment函数来实现这一算法。

匈牙利算法是用于求解指派问题(Assignment Problem)的一种多项式时间算法。它特别适合解决成本矩阵为方阵的任务分配问题,但也可以处理矩阵为非方阵的情况(通过填充虚拟行或列)。算法的核心思想是通过对成本矩阵进行行和列的优化,最终找到一个使总成本最小的任务到资源分配方案

linear_sum_assignment函数的实现,在Pyhton语言中可以直接实现匈牙利算法,接受一个成本矩阵的输入,输出两个数:row_indcol_ind,它们分别表示任务(行)和资源(列)的最优分配索引。

  • row_ind 是任务的索引。
  • col_ind是资源的索引,与row_ind 相对应

生成配对关系

list(zip(row_ind,col_ind))将任务和资源的索引配对在一起,并且返回一个包含所有配对的列表,这个列表表示最优的任务到资源的分配方式。

计算总成本

cost[row_ind, col_ind].sum()提取所有的最优分配的成本,并计算这些成本的总和,sum函数对成本进行求和,得到最小化的目标函数值,也就是最优分配下的总成本。
具体代码

import numpy as np
from scipy.optimize import linear_sum_assignment
# 损耗矩阵
cost = np.array([[66.8, 75.6, 87, 58.6],
                 [57.2, 66, 66.4, 53],
                 [78, 67.8, 84.6, 59.4],
                 [70, 74.2, 69.6, 57.2],
                 [67.4, 71, 83.8, 62.4]])
row_ind, col_ind = linear_sum_assignment(cost)
row_ind
col_ind
# 目标成员坐标
list(zip(row_ind,col_ind))
# 目标函数值
cost[row_ind, col_ind].sum()

代码的应用场景

这段代码常用于解决分配问题,比如在多个任务需要分配给多个工人(或机器、资源等)的场景下,寻找最优的分配方式以最小化总成本。这在运输问题、作业调度、以及资源分配等实际应用中非常有用。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自由自在2004

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值