PuLP库-多数线性规划问题

news2024/10/2 20:28:51

投标价格重预算

背景

甲方需要采购一批物资,采购数量为甲方给定的预计采购数量,并限制了采购总价。例甲方采购预算清单如下,采购总预算为不超过 3175 元

采购内容采购数量投标单价投标报价合计
电脑10
空调20
洗衣机8
桌子7
打印机35
合计

注:乙方根据以上预算清单填报单价,最终数量按实结算,单价不变

我方竞标时在甲方预算清单内填报单价,假设我方报价如下:

采购内容采购数量投标单价投标报价合计
电脑1015150
空调2020400
洗衣机835280
桌子775525
打印机35521820
合计3175

最终项目实施完毕后,结算是根据实际实施数量*投标单价进行结算,根据经验我们能判断最终那些数量会增加实施,那些数量会减少实施,假设实际实施数量如下(电脑增加了 3 台,空调减少了 2 台…)

采购内容采购数量投标单价投标报价合计
电脑13
空调18
洗衣机7
桌子7
打印机38
合计3175

由此产生了需求,求出在总报价不变的情况下,针对最终实施数量会减少的部分,尽可能的报低单价,针对最终实施数量会增加的部分报高单价,对于各个商品的单价变化幅度有一个同一的范围,以达到结算的时候利益最大化。

最终需求:在已知最终数量的情况下,报价单价策略应该填多少,利益才能最大化,也就是我们需要求的那个最大值。

img

问题分析

我们对上面图片中的内容进行划分,从左到右分别为A、B、C、D、E、F、G列,D列各行的值为B*C,在最后还有对于所有采购商品的总价汇总。A、B、C、E列的值已经给出,现在需要求出D、F、G列的内容,此处略过D列数据的求解,着重分析F列的求值。
假设X列从上至下的值为一个数组 $(x_1, x_2, …, x_n) ,那么 F 列的值为 ,那么F列的值为 ,那么F列的值为(f_1, f_2, …, f_n)$,B列的值为(b_1, b_2, …, b_n),C列的值为(c_1, c_2, …, c_n),按照题目中所给的条件,我们可以得到以下两个约束条件:

  1. 一个1 * n的矩阵B与一个n * 1的矩阵C点乘后的结果 == 一个1 * n的矩阵B与一个n * 1的矩阵F点乘的结果
  2. f i ∗ ( 1 − m a x D e c r e a s e ) < = f i < = f i ∗ ( 1 + m a x I n c r e a s e ) f_i * (1 - maxDecrease) <= f_i <= f_i * (1 + maxIncrease) fi(1maxDecrease)<=fi<=fi(1+maxIncrease)
  3. s u m = ∑ i = 1 n f i sum = \sum_{i=1}^n f_i sum=i=1nfi

仔细一分析过后,我们可以发现我们仔细分析了一下,我们可以发现,这是一个线性规划问题,没错,就是高中时期常常出现在填空题里的那个属于送分题的线性规划问题,只不过从高中时期的不超过4个限制条件变成了n个而已,没有什么难的。此时我们开始思考一个问题,那就是如何构造一个多数线性规划模型,并能够针对限制条件数量未知的情况来进行模型的快速调用,并实现限制条件和结果的输入以及得出。

所幸,Python中有这么一个库,能够实现我们当前面临问题的完美解决。

相关依赖库

主要介绍实现过程中的几个重要库,其余库的具体安装要求请参照项目下的requirements.txt文件,以下是关于几个重要库的介绍。

PuLP

如果你在百度里搜索Python PuLP,你会发现与之相关联的词条除了一个同名的乐队之外,还有优化问题以及混合整数规划(MILP)这两个词条。如果你在google里面搜索同样的词条,至少前一页都是Pulp库以及线性规划相关的内容。不同的搜索引擎都能找到的共同点就是,pulp在线性规划问题方面的使用。

在pulp库的文档中你可以看到有这么一个关于猫粮中原料配比的问题,如果你看不懂英文,可以看下面的这个文档,这是某个知乎上的前人写的说明,基本上已经将原本文档中的内容进行了翻译,大家可以着重看代码部分:

# 导入 PulP
from pulp import *

# 建立线性规划问题,指定名称:CatFood, 问题的目标:求解最小值 LpMinimize
prob = pulp.LpProblem(name='CatFood', sense=LpMinimize)

# 定义变量: 鸡肉占比,设置下限值为 0 , 不能是负数
x1 = LpVariable("鸡肉占比", lowBound=0)

# 定义变量: 牛肉占比,设置下限值为 0 , 不能是负数
x2= LpVariable("牛肉占比", lowBound=0)

# 将目标函数用 += 方式附加到 prob 变量
prob += 0.013*x1 + 0.008*x2, "最小成本"

# 将约束条件用 += 方式附加到 prob 变量,注意区别是约束条件有判断操作符
prob += x1 + x2 == 100, "占比总和"
prob += 0.100 * x1 + 0.200 * x2 >= 8.0, "蛋白质含量"
prob += 0.080 * x1 + 0.100 * x2 >= 6.0, "脂肪含量"
prob += 0.001 * x1 + 0.005 * x2 <= 2.0, "纤维含量"
prob += 0.002 * x1 + 0.005 * x2 <= 0.4, "盐含量"

# 将问题输出为 lp 文件
prob.writeLP('catfood.lp')

此处并没有将问题进行解决,只是通过代码的描述,将问题的内容实现了自生成,你会得到一个catfood.lp文件,里面的内容长这样:

# 查看输出的 lp 文件
! cat catfood.lp
\* CatFood *\
Minimize
最小成本: 0.008 牛肉占比 + 0.013 鸡肉占比
Subject To
占比总和: 牛肉占比 + 鸡肉占比 = 100
盐含量: 0.005 牛肉占比 + 0.002 鸡肉占比 <= 0.4
纤维含量: 0.005 牛肉占比 + 0.001 鸡肉占比 <= 2
脂肪含量: 0.1 牛肉占比 + 0.08 鸡肉占比 >= 6
蛋白质含量: 0.2 牛肉占比 + 0.1 鸡肉占比 >= 8
End

是的,他没什么用处,只是给我们看看的。but,这只是这个知乎的作者没有认真思考照单全抄的缘故,因为此时的我们完全可以将上面的问题通过当前库进行解决(一个库如果只能将问题进行描述但不能实现解决,这就相当于上厕所不仅没有纸而且没有水),只需再加几行代码:

# 用求解器解决问题
prob.solve()

# 查看求解器的状态
# 返回的状态是 : Not Solved, Infeasible, Unbounded, Undefined, Optimal
# Optimal 就是有最优解
print("Status:", LpStatus[prob.status])
# 查看变量的值
for v in prob.variables():
    print(v.name, "=", v.varValue, '%')

print( "每100克猫粮的最小成本 = ", value(prob.objective))

添加以上代码后可以实现在控制台输出最终的结果。

此时,我们又面临一个问题,如何添加多个参数,可能很多很多个参数,是的,这个问题有点让人头秃,but,在这里,我不得不说,写了这个库的人真是个天才,因为他提供了一个addVariable()方法,使得我们可以通过遍历已有的数据实现对问题限制条件的批量化添加,比如这样:

    for i in range(10):
        v = pulp.LpVariable(key_list[i], lowBound=original_price[i], upBound=original_price[i] * (1 + max_increase),
                            cat='Continuous')
        MyProblem.addVariable(v)

其中key_list中元素为str类型,original_price中元素为数字类型,max_increase是数字。

最终,我们通过构建这个规划问题,实现了投标单价修改值的求解。具体代码由于当前代码属于商用的开发阶段,不方便直接展示,具体实现请查看version包下的budget_bid_price_version_01.py文件,所有的代码已上传到github仓库。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1412773.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Shell脚本④循环语句for、while、until

目录 一.for 1. 九九乘法表 2.求1到10奇数和 3.累计加到100 &#xff08;1&#xff09;方法一 &#xff08;2&#xff09;方法2 二.while 1.猜价格小游戏 2.累加到100的方法三 三.until循环 1.累加到100方法四 四.嵌套循环 五.循环语句中break、exit和continue 1…

网络防御——NET实验

一、实验拓扑 二、实验要求 1、生产区在工作时间&#xff08;9&#xff1a;00---18&#xff1a;00&#xff09;内可以访问服务区&#xff0c;仅可以访问http服务器&#xff1b; 2、办公区全天可以访问服务器区&#xff0c;其中&#xff0c;10.0.2.20可以访问FTP服务器和HTTP服…

Google浏览器Provisional headers are shown. Disable cache to see full headers.

Google浏览器Provisional headers are shown. Disable cache to see full headers. 问题 解决方法 勾选禁用缓存&#xff0c;刷新 成功

制造领域 物料清单(BOM)与零件明细表的区别

有许多人分不清物料清单(BOM)与零件明细表的区别,其实它们在企业的生产管理软件中起着不同的作用,各有各的特色,但是却有不尽相同。接下来我们就来区分一下吧 物料清单(BOM)&#xff0c;是详细记录一个项目所用到的所有下阶材料及相关属性&#xff0c;亦即母件与所有子件的从属…

代码随想录刷题笔记 DAY15 | 翻转二叉树 No.226 | 对称二叉树 No.101

Day 15 01. 翻转二叉树&#xff08;No. 226&#xff09; 题目链接 代码随想录题解 1.1 题目 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9…

代码随想录 Leetcode429. N 叉树的层序遍历

题目&#xff1a; 代码(首刷自解 2024年1月24日&#xff09;&#xff1a; /* // Definition for a Node. class Node { public:int val;vector<Node*> children;Node() {}Node(int _val) {val _val;}Node(int _val, vector<Node*> _children) {val _val;childre…

在云服务器上通过FileZilla配置FTP(通过FileZilla配置FTP升级版)

有兴趣的读者可以看看博主的博客&#xff0c;有很全面的教程 阿里云之申请云服务器–配置jdk,tomcat,安全策略–能够在他人电脑上显示本电脑的Tomcat 通过FileZilla配置FTP 修改我们的安全组&#xff0c;将21&#xff0c;和50000-50010端口添加进去 加入实例即可&#xff0c;剩…

怎样使用CSS技术美化网页?(知识点2)

知识引入 引入CSS样式表 CSS提供了行内式、内嵌式、外链式、导入式四种引入方式&#xff0c;具体介绍如下。 行内式 行内式也被称为内联样式&#xff0c;是通过标签的style属性来设置标签的样式&#xff0c;其基本语法格式如下。 <标签名 style“属性&#xff1a;属性值…

Denoising diffusion implicit models 阅读笔记2

Denoising diffusion probabilistic models (DDPMs)从马尔科夫链中采样生成样本&#xff0c;需要迭代多次&#xff0c;速度较慢。Denoising diffusion implicit models (DDIMs)的提出是为了在复用DDPM训练的网络的前提下&#xff0c;加速采样过程。 加速采样的基本思路是&#…

Shell脚本⑤函数与数组

一.函数 封装的可重复利用的具有特定功能的代码 格式&#xff1a; 方法一&#xff1a; [function] 函数名 (){ 命令序列 [return x] #使用return或者exit可以显式的结束函数 } 方法二&#xff1a; 函数名(){ 命令序列 } 1.函数的调用方法 &#xff08;1&…

【Flink】记录Flink 任务单独设置配置文件而不使用集群默认配置的一次实践

前言 我们的大数据环境是 CDP 环境。该环境已经默认添加了Flink on Yarn 的客户端配置。 我们的 Flink 任务类型是 Flink on Yarn 的任务。 默认的配置文件是在 /etc/flink/conf 目录下。如今我们的需求是个别任务提供的配置仅用于配置执行参数&#xff0c;例如影响作业的配置…

python 基础知识点(蓝桥杯python科目个人复习计划24)

今日复习内容&#xff1a;基础算法中的模拟 1.模拟题 &#xff08;1&#xff09;定义&#xff1a;直接按照题目含义模拟即可&#xff0c;一般不涉及算法。 &#xff08;2&#xff09;注意&#xff1a;读懂题&#xff1a;理清楚题目流程&#xff1b; 代码和步骤一一对应&…

爷爷问:IPv6为什么还没有完全替换掉IPv4 ?区别是什么?

首先IPV6之所以提出&#xff0c;很大一部分原因是IPV4的地址不够用了&#xff0c;再出现新设备就没得地址分配&#xff0c;所以才会提出新的IP版本以满足分配需求。在对IPV6进行一个基本了解后来看看两者的区别。 IPV6基本了解 1.层次化的地址结构 地址长度为128bit&#xf…

鸿蒙ArkUI开发-实现增删Tab页签

场景介绍 部分应用在使用过程中需要自定义添加或删除标签的场景&#xff0c;比如在浏览器中的顶部标签栏中需要新打开或关闭网页页签&#xff0c;而这种场景与Tabs组件效果类似&#xff0c;但Tabs组件不提供增加或删除页签的功能&#xff0c;不能自由的增加删除页签&#xff0…

Java编程的利器:Pair和Triple无缝解决多值返回问题,助力编写高效代码

在实际编码中&#xff0c;经常会遇到一个方法需要返回多个值的情况&#xff0c;你编写一个方法&#xff0c;需要同时返回某个操作的结果和一些相关的附加信息。使用传统的方式&#xff0c;你可能需要创建一个包含这些信息的自定义类或者使用集合&#xff08;如 Map&#xff09;…

系统引导程序 Boot Loader——学习笔记

基于嵌入式Linux 的完整系统软件由三个部分组成&#xff1a;系统引导程序、Linux 操作系统内核和文件系统。 系统引导程序 Boot Loader 是系统加电后运行的第一段软件代码&#xff0c;它的作用是加载操作系统或者其他程序到内存中&#xff0c;并将控制权交给它们。 Boot Load…

代码随想录算法训练营第16天 | 104.二叉树的最大深度, 111.二叉树的最小深度 ,222.完全二叉树的节点个数

二叉树理论基础&#xff1a; https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE 104.二叉树的最大深度 题目链接&#xff1a;https://leetcode.cn/problems/maximum-depth-…

Mediasoup Demo-v3笔记(三)——Mediasoup库介绍

Mediasoup基本概念 Worker &#xff1a; 每一个worker就是一个进程&#xff08;节点&#xff09;&#xff0c;进程和进程之间可以通信Router&#xff1a;每一个Router就是一个房间的概念Producer&#xff1a;每一个发出声音和视频的流都是一个ProducerConsumer&#xff1a;每一…

Confluence 的文章导入到 YouTrack KB 中

YouTrack 是有一个 KB 的&#xff0c;我们可以吧 Confluence 的文章全部导入到 YouTrack 的 KB 中。 首先&#xff0c;你需要具有管理员权限&#xff0c;然后选择导入。 然后可以在打开的界面中新增一个导入。 在新增导入中输入 Confluence 在随后的界面中输入你 Confluence …

浅谈ICMP协议

ICMP(Internet Control Message Protocol) 网络控制消息协议是网络层的协议&#xff0c;所谓控制&#xff0c;指的是通过下发指令来判断是否当前主机可达目标主机及不可达时的错误报告。通常使用ping命令和traceroute命令来使用。 ping命令:检测到目标主机是否可达、 tracerout…