python的仿真效果好吗_Python SimPy 仿真系列 (1)

本系列文章旨在介绍 SimPy 在工业仿真中的应用。

在物流行业/工厂制造业/餐饮服务业存在大量急需优化的场景, 例如:如何最优化快递分拣人员的排班表以满足双十一突发的快递件量

如何估算餐厅在用餐高峰的排队时长

估算特定工序下,工厂生产所需要的物料成本/人力成本/时间成本

这类场景无法通过常规算法求出最优解, 但是我们可以通过大量业务实践中总结出一些接近的次优解。

实际生产中,随时调整厂房的生产线来试验最优解是非常昂贵的。引进仿真技术,可以给业务研究员无限的自由度去调整验证不同的优化方案。仿真的成本无非是计算机的算力,以及程序员编写业务逻辑的时间。

行业上其实已经存在一些工业仿真软件。但这类仿真软件往往针对某些特定场景高度定制化,数据埋点往往不全,缺乏通用的数据库接口,难以结合真实业务产生的数据进行仿真,这样就失去与真实业务进行比较的可能。

利用 SimPy 我们可以构建一套完全开源的仿真方案,可以完全私有定制业务场景。利用 Python 强大的生态,仿真数据从来源到输出分析,可以衔接所有开源流行的数据分析框架。

目前,我们已经利用 SimPy 仿真模拟物流核心分拣业务,结合 MySQL,Tableau,Pandas,Spark 构建一整套完整的报表可视化分析体系,已经能够成功应用于现代物流中,为分拣业务提供持续优化改良方案。

我们创造性地解决了一些原有软件仿真中欠缺的环节,这些内容将会在接下来的文章中分享。

作为本系列文章的开篇,我们将简要地介绍 SimPy 框架的基本理念。

官方资料

SimPy 是一个基于标准 Python 以进程为基础的离散事件仿真框架。

SimPy 中的进程是由 Python 生成器构成,生成器的特性可以模拟具有主动性的物件,比如客户、汽车、或者中介等等。SimPy也提供多种类的共享资源(shared resource)来描述拥挤点(比如服务器、收银台和隧道)。

仿真运行速度非常快,仿真中的模拟时间长短不影响仿真运行效率,仿真中的模拟时间单位可以任意指定,一秒、一年、一小时都是允许的。

SimPy 安装

SimPy 可以同时在 Python 2 (>=2.7)以及 Python 3(>=3.2)上运行。只要有pip,轻松安装。“$ pip install simpy”

手动安装 SimPy 也非常方便。提取存档,打开存放 SimPy 的 terminal 窗口,然后输入:“$ python setup.py install”

你可以选择性地运行 SimPy 测试文件以了解软件是否可行。前提是要安装 pytest 包。并在 SimPy 的安装路径下运行下列命令行:“$ py.test --pyargs simpy”

SimPy 核心概念

SimPy 是离散事件驱动的仿真库。所有活动部件,例如车辆、顾客,、即便是信息,都可以用 process (进程) 来模拟。这些 process 存放在 environment (环境) 。所有 process 之间,以及与environment 之间的互动,通过 event (事件) 来进行.

process 表达为 generators (生成器), 构建event(事件)并通过 yield 语句抛出事件。

当一个进程抛出事件,进程会被暂停,直到事件被激活(triggered)。多个进程可以等待同一个事件。 SimPy 会按照这些进程抛出的事件激活的先后, 来恢复进程。

其实中最重要的一类事件是 Timeout, 这类事件允许一段时间后再被激活, 用来表达一个进程休眠或者保持当前的状态持续指定的一段时间。这类事件通过 Environment.timeout来调用。

Environment

Environment 决定仿真的起点/终点, 管理仿真元素之间的关联, 主要 API 有simpy.Environment.process - 添加仿真进程

simpy.Environment.event - 创建事件

simpy.Environment.timeout - 提供延时(timeout)事件

simpy.Environment.until - 仿真结束的条件(时间或事件)

simpy.Environment.run - 仿真启动

样例代码说明 API:

下面是来自官方文档的两个例子:第一个例子, 描述如何定义一个进程, 并添加到 env 内, 简单展示启动仿真的代码结构

第二个例子, 描述一个汽车驾驶一段时间后停车充电, 汽车驾驶进程和电池充电进程通过事件的激活来相互影响

Example 1

import simpy

# 定义一个汽车进程

def car(env):

while True:

print('Start parking at %d' % env.now)

parking_duration = 5

yield env.timeout(parking_duration) # 进程延时 5s

print('Start driving at %d' % env.now)

trip_duration = 2

yield env.timeout(trip_duration) # 延时 2s

# 仿真启动

env = simpy.Environment() # 实例化环境

env.process(car(env)) # 添加汽车进程

env.run(until=15) # 设定仿真结束条件, 这里是 15s 后停止

Example 2

from random import seed, randint

seed(23)

import simpy

class EV:

def __init__(self, env):

self.env = env

self.drive_proc = env.process(self.drive(env))

self.bat_ctrl_proc = env.process(self.bat_ctrl(env))

self.bat_ctrl_reactivate = env.event()

self.bat_ctrl_sleep = env.event()

def drive(self, env):

"""驾驶进程"""

while True:

# 驾驶 20-40 分钟

print("开始驾驶 时间: ", env.now)

yield env.timeout(randint(20, 40))

print("停止驾驶 时间: ", env.now)

# 停车 1-6 小时

print("开始停车 时间: ", env.now)

self.bat_ctrl_reactivate.succeed() # 激活充电事件

self.bat_ctrl_reactivate = env.event()

yield env.timeout(randint(60, 360)) & self.bat_ctrl_sleep # 停车时间和充电程序同时都满足

print("结束停车 时间:", env.now)

def bat_ctrl(self, env):

"""电池充电进程"""

while True:

print("充电程序休眠 时间:", env.now)

yield self.bat_ctrl_reactivate # 休眠直到充电事件被激活

print("充电程序激活 时间:", env.now)

yield env.timeout(randint(30, 90))

print("充电程序结束 时间:", env.now)

self.bat_ctrl_sleep.succeed()

self.bat_ctrl_sleep = env.event()

def main():

env = simpy.Environment()

ev = EV(env)

env.run(until=300)

if __name__ == '__main__':

main()

Resource 和 Store

Resource/Store 也是另外一类重要的核心概念, 但凡仿真中涉及的人力资源以及工艺上的物料消耗都会抽象用 Resource 来表达, 主要的 method 是 request. Store 处理各种优先级的队列问题, 表现跟 queue 一致, 通过 method get / put 存放 item

Store - 抽象队列simpy.Store - 存取 item 遵循仿真时间上的先到后到

simpy.PriorityStore - 存取 item 遵循仿真时间上的先到后到同时考虑人为添加的优先级

simpy.FilterStore - 存取 item 遵循仿真时间上的先到后到, 同时队列中存在分类, 按照不同类别进行存取

simpy.Container - 表达连续/不可分的物质, 包括液体/气体的存放, 存取的是一个 float 数值

Resource - 抽象资源simpy.Resource - 表达人力资源或某种限制条件, 例如某个工序可调用的工人数, 可以调用的机器数

simpy.PriorityResource - 兼容Resource的功能, 添加可以插队的功能, 高优先级的进程可以优先调用资源, 但只能是在前一个被服务的进程结束以后进行插队

simpy.PreemptiveResource - 兼容Resource的功能, 添加可以插队的功能, 高优先级的进程可以打断正在被服务的进程进行插队

样例代码说明 API:

Example 3

"""

银行排队服务例子

情景:

一个柜台对客户进行服务, 服务耗时, 客户等候过长会离开柜台

"""

import random

import simpy

RANDOM_SEED = 42

NEW_CUSTOMERS = 5 # 客户数

INTERVAL_CUSTOMERS = 10.0 # 客户到达的间距时间

MIN_PATIENCE = 1 # 客户等待时间, 最小

MAX_PATIENCE = 3 # 客户等待时间, 最大

def source(env, number, interval, counter):

"""进程用于生成客户"""

for i in range(number):

c = customer(env, 'Customer%02d' % i, counter, time_in_bank=12.0)

env.process(c)

t = random.expovariate(1.0 / interval)

yield env.timeout(t)

def customer(env, name, counter, time_in_bank):

"""一个客户表达为一个协程, 客户到达, 被服务, 然后离开"""

arrive = env.now

print('%7.4f %s: Here I am' % (arrive, name))

with counter.request() as req:

patience = random.uniform(MIN_PATIENCE, MAX_PATIENCE)

# 等待柜员服务或者超出忍耐时间离开队伍

results = yield req | env.timeout(patience)

wait = env.now - arrive

if req in results:

# 到达柜台

print('%7.4f %s: Waited %6.3f' % (env.now, name, wait))

tib = random.expovariate(1.0 / time_in_bank)

yield env.timeout(tib)

print('%7.4f %s: Finished' % (env.now, name))

else:

# 没有服务到位

print('%7.4f %s: RENEGED after %6.3f' % (env.now, name, wait))

# Setup and start the simulation

print('Bank renege')

random.seed(RANDOM_SEED)

env = simpy.Environment()

# Start processes and run

counter = simpy.Resource(env, capacity=1)

env.process(source(env, NEW_CUSTOMERS, INTERVAL_CUSTOMERS, counter))

env.run()

Example 4

# python 3.6 with SimPy

"""

工厂工序和传送带

情景:

一个机器处理物件, 处理完毕后放上传送带, 传送带传送一段时间后到达下个一个机器设备.

[last_q][machine1] ----[con_belt]----> [next_q][machine2]

"""

import simpy

import random

PROCESS_TIME = 0.5 # 处理时间

CON_BELT_TIME = 3 # 传送带时间

WORKER_NUM = 2 # 每个机器的工人数/资源数

MACHINE_NUM = 2 # 机器数

MEAN_TIME = 0.2 # 平均每个物件的到达时间间距

def con_belt_process(env,

con_belt_time,

package,

next_q):

"""模拟传送带的行为"""

while True:

print(f"{round(env.now, 2)} - item: {package} - start moving ")

yield env.timeout(con_belt_time) # 传送带传送时间

next_q.put(package)

print(f"{round(env.now, 2)} - item: {package} - end moving")

env.exit()

def machine(env: simpy.Environment,

last_q: simpy.Store,

next_q: simpy.Store,

machine_id: str):

"""模拟一个机器, 一个机器就可以同时处理多少物件 取决资源数(工人数)"""

workers = simpy.Resource(env, capacity=WORKER_NUM)

def process(item):

"""模拟一个工人的工作进程"""

with workers.request() as req:

yield req

yield env.timeout(PROCESS_TIME)

env.process(con_belt_process(env, CON_BELT_TIME, item, next_q))

print(f'{round(env.now, 2)} - item: {item} - machine: {machine_id} - processed')

while True:

item = yield last_q.get()

env.process(process(item))

def generate_item(env,

last_q: simpy.Store,

item_num: int=100):

"""模拟物件的到达"""

for i in range(item_num):

print(f'{round(env.now, 2)} - item: item_{i} - created')

last_q.put(f'item_{i}')

t = random.expovariate(1 / MEAN_TIME)

yield env.timeout(round(t, 1))

if __name__ == '__main__':

# 实例环境

env = simpy.Environment()

# 设备前的物件队列

last_q = simpy.Store(env)

next_q = simpy.Store(env)

env.process(generate_item(env, last_q))

for i in range(MACHINE_NUM):

env.process(machine(env, last_q, next_q, machine_id=f'm_{i}'))

env.run()

结语

文章暂时结束,文章主要通过代码来展示 SimPy 的仿真能力。

接下来的文章计划是:介绍如何构建一个基于时间动态的人力资源排班表,来模拟不同时段人力的分布,比如流水线上不同岗位在不同的时间段需求的人力资源是不均等,我们可以通过调岗的形式来达到人力资源调优,让人力资源在时间分布上最优。

介绍如何一个队列,同时实现时间先后优先和优先级上优先,来模拟比如银行柜台客户排队 vip 进行插队的情景

启动一个翻译 SimPy 官方文档的众包计划,希望在国内降低大众学习 SimPy 的语言成本

本文作者:

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

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

相关文章

jax-rs jax-ws_您的JAX-RS API并非天生就等于:使用动态功能

jax-rs jax-ws这次,我们将讨论一些有关JAX-RS 2.0 API的内容,并涉及规范的一个非常有趣的方面: 动态功能及其实用性。 传统上,当配置和部署JAX-RS 2.0 API(使用Application类,从servlet引导或通过RuntimeD…

10 NAT网络地址转换

广域网技术 上面聊的内容都是内网的一些配置,但内网终将要访问外网的,我们需要怎么处理呢?一般使用HDLC(高级数据链路控制协议)或者PPP(点对点协议)。 使用PPP安全接入Internet PPP&#xff0…

Leetcode 61. 旋转链表

原题链接 题解: 1.首先确定k的取值,可能超过链表长度,所有对 k%n 来确定 2.需要找到k前面的结点和最右位置的结点,可以使用双指针得到。 /*** Definition for singly-linked list.* struct ListNode {* int val;* Lis…

java应用性能指标_性能与可靠性:Java应用为何像F1汽车

java应用性能指标再想一想。 性能和可靠性相关吗? 还是这些东西相互排斥? 我认为是后者。 如今,现实是IT部门将应用程序的性能和可靠性视为同一事物,但这离事实还差得远。 让我们看看一级方程式车队如何管理性能和可靠性。 上赛…

tomcat ajp协议安全限制绕过漏洞_Apache Tomcat文件包含漏洞(CVE20201938)复现

一、漏洞背景2020年02月20日,国家信息安全漏洞共享平台(CNVD)发布了关于Apache Tomcat文件包含漏洞(CVE-2020-1938/CNVD-2020-10487)的安全公告。Tomcat作为一款免费开源轻量级的web应用服务器,广泛应用于并发量不是很高的场合,Tomact默认端口…

【H.264/AVC视频编解码技术】第三章【熵编码】

熵编码的概念 熵:化学与热力学概念,用于度量能量退化的指标。熵越高,物体/系统做工能力越低。 信息学中的熵:用于度量消息的平均信息量,和信息的不确定性。越是随机的,前后不相关的信息,其熵越…

mock 抛出一个异常如何终止_教你使用Mock完成单元测试

更多精彩文章请关注本人微信公众号1、什么是Mock?mock是在测试过程中,对于一些不容易构造/获取的对象,创建一个mock对象来模拟对象的行为。比如说你需要调用B服务,可是B服务还没有开发完成,那么你就可以将调用B服务的那部分给Moc…

jconsole查看连接数_在JConsole和VisualVM中查看DiagnosticCommandMBean

jconsole查看连接数我已经将JConsole用作合适的通用JMX客户端已有很多年了。 该工具通常随Oracle JDK一起提供,并且易于使用。 在JMX交互方面,JConsole优于VisualVM的最大优点是JConsole带有内置的MBeans选项卡,而必须为VisualVM中的相同功能…

剑指 Offer 40-----50

50. 第一个只出现一次的字符 原题链接 class Solution { public:char firstUniqChar(string s) {char res ;if(!s.size()) return res;unordered_map<char, int> mmp; for(int i 0; i < s.size(); i) {mmp[s[i]] 1;}for(int i 0; i < s.size(); i){if(mmp[s…

jpa root.join_JPA 2.1和Java EE 7中的JPQL增强功能(第1部分– JOIN ON)

jpa root.joinJava EE 7已经存在了几年&#xff0c;并且提供了一些非常有用且期待已久的功能&#xff0c;例如实体图以及对存储过程和结果映射的更好支持。 有关概述&#xff0c;请参阅Thorben Janssen的博客文章 。 但是&#xff0c;我想添加有关JPA查询语言功能的更详细的摘要…

如何知道一个域名是否存在_域名检测API实现查看一个域名在微信中是否被封

针对微信转发分享链接过程中&#xff0c;您宝贵的域名被微信检测系统过滤拦截而无法正常浏览&#xff0c;例如该网页包含诱导分享内容&#xff0c;被多人投诉等&#xff0c;又例如提示该网页已停止访问等提示。怎么查询域名在微信中是否被封了呢?以上接口可检测到域名的四种异…

LeetCode题集大全

LeetCode 01. 两数之和 LeetCode 02.两数相加 LeetCode 03. 无重复字符的最长子串 LeetCode 07. 整数反转 Leetcode 08. 字符串转换整数 (atoi) LeetCode 09. 回文数 LeetCode 13. 罗马数字转整数 LeetCode 15 二进制中1的个数 Leetcode 19. 删除链表的倒数第N个节点 Le…

qt4.8创建.pri_注意Java 8的[Pri​​mitive] Stream.iterate()中的递归

qt4.8创建.priTagir Valeev关于Stack Overflow的一个有趣问题最近引起了我的注意。 为了简短起见&#xff08;请阅读问题的详细信息&#xff09;&#xff0c;而以下代码则有效&#xff1a; public static Stream<Long> longs() {return Stream.iterate(1L, i ->1L l…

量化指标公式源码_通达信指标公式源码线上阴线指标公式

工作线:(EMA(C,14)),POINTDOT,LINETHICK3,COLOR22ACDE;生命线:(MA(C,25)),LINETHICK1,COLORMAGENTA;不惑线:(MA(C,40)),COLORCYAN,LINETHICK1;姊妹线:(EMA(C,56)),POINTDOT,COLOR33CCDD,LINETHICK1;A3:EMA((((SLOPE(C,21)) * 20) C),68);A4:EMA(C,10);A5:REF(C,2);A6:((SMA((M…

归并排序 自带时间复杂度测试

//时间复杂度 O(N*log2N) //稳定程度&#xff1a; 稳定 /* 确定分界点&#xff0c;中间位置 两端排序 归并&#xff0c;合二为一 */#include<iostream> #include<time.h> using namespace std; int tmp[250001]; void Sort(int List[], int l, int r);int main()…

python 图像变化检测_python hough变换检测直线的实现方法

1 原理2 检测步骤将参数空间(ρ,θ) 量化成m*n(m为ρ的等份数&#xff0c;n为θ的等份数)个单元&#xff0c;并设置累加器矩阵&#xff0c;初始值为0&#xff1b;对图像边界上的每一个点(x,y)带入ρxcosθysinθ&#xff0c;求得每个θ对应的ρ值&#xff0c;并在ρ和θ所对应的…

ruby elixir_如何使用Elixir和Phoenix快速入门构建CRUD REST API

ruby elixir这篇文章将展示如何使用Elixir和Phoenix框架构建REST API。 重点将是为持久化到Postgres数据库后端的模型提供CRUD&#xff08;创建&#xff0c;读取&#xff0c;更新&#xff0c;删除&#xff09;端点。 我应该警告你&#xff1b; 这是一个简单的例子。 但是&#…

LeetCode 07. 整数反转

原题描述 个人解法思路 class Solution { public:int reverse(int x) {int temp0;//需要返回的最后结果while(x!0){if(temp>INT_MAX/10 || temp<INT_MIN/10)return 0;temptemp*10x%10; //个位变十位 同理xx/10; //进行降位} return temp;} };

苹果开发者账号可以创建多少测试证书_ios苹果企业账号你所不知道的那些点

在2019年的时候苹果审核机制一下子就变得越来越严格了&#xff0c;现在我们想要通过苹果的官方网站去申请一个全新的ios苹果企业账户就是会非常的困难&#xff0c;很多公司有的也没有达到资格&#xff0c;ios开发商在申请苹果企业账号的时候&#xff0c;一方面需要像苹果提供大…

slf4j 记录日志文件_教程:正确的SLF4J日志记录用法以及如何检查它

slf4j 记录日志文件SLF4J是一个非常流行的日志记录外观&#xff0c;但是&#xff0c;就像我们使用的所有库一样&#xff0c;我们有可能以错误的方式或至少以一种非最佳方式使用它。 在本教程中&#xff0c;我们将列出常见的日志记录错误以及如何使用FindBugs检测到它们。 我们…