SimPy:用 Python 模拟真实世界的进程

举报
Yuchuan 发表于 2021/12/11 21:33:55 2021/12/11
【摘要】 在本教程中,您学习了如何使用该框架在 Python 中构建和运行模拟simpy。您已经了解系统如何让代理进行处理,以及如何创建这些系统的虚拟表示以加强它们免受拥塞和延迟的影响。虽然模拟的类型可能会有所不同,但整体执行是相同的!您将能够将在这里学到的知识应用到各种不同的场景中。

目录

现实世界充满了机场和高速公路等经常出现拥堵和延误的系统。当这些系统没有优化时,它们的低效率会导致无数不满意的客户和时间的浪费。在本教程中,您将学习如何使用 Python 的simpy框架来创建虚拟模拟,以帮助您解决此类问题。

在本教程中,您将学习如何:

  • 使用模拟来模拟真实世界的过程
  • 创建逐步算法来逼近复杂系统
  • 使用 Python设计和运行真实世界的模拟simpy

什么是模拟

一个模拟的真实世界系统的表示。人们可以使用这个系统的数学或计算模型来研究它是如何工作的,或者当它的一部分发生变化时会发生什么。模拟用于机场、餐厅、机械师、政府机构和许多其他系统,在这些系统中,资源分配不当会导致拥堵、客户不满和严重的运输延误。

一个系统可以在那里的事情发生在任何环境。实际系统的示例包括洗车、银行、制造工厂、机场、邮局、呼叫中心等。这些系统具有在其中进行过程的代理。例如:

  • 洗车将让汽车经过洗涤过程。
  • 机场将让乘客通过安全检查程序。
  • 呼叫中心将让客户完成与电话推销员交谈的过程。

这种关系总结在下表中:

System Agent Process
Car wash Car Wash
Airport Passenger Security check
Call center Customer Speak with a telemarketer

了解代理在系统内经历的流程是物流规划的重要组成部分,尤其是对于大型组织而言。例如,如果当天没有足够的工作人员,机场可以看到乘客在安全检查站的等待时间猛增。类似地,如果路由不正确,对时间敏感的邮件可能会延迟数天(甚至数周)。

这些拥塞实例可能会对时间和金钱产生现实影响,因此能够预先对这些过程进行建模非常重要。这让您了解系统可能在哪里遇到问题,以及应如何提前分配资源以尽可能最有效地解决这些问题。

模拟的工作原理

在 Python 中,您可以使用该simpy框架进行事件模拟。首先,快速了解一下模拟过程如何在 Python 中运行。下面是模拟安全检查点系统的代码片段。以下三行代码设置环境,传递所有必要的功能,并运行模拟:

# Set up the environment
env = simpy.Environment()

# Assume you've defined checkpoint_run() beforehand
env.process(checkpoint_run(env, num_booths, check_time, passenger_arrival))

# Let's go!
env.run(until=10)

上面的第一行代码建立了环境。您将通过分配simpy.Environment()给所需的变量来完成此操作。在这里,它被简单地命名为env。这告诉simpy创建一个名为的环境对象env,该对象将管理模拟时间并在每个后续时间步长中移动模拟。

建立环境后,您将传入所有将作为参数的变量。这些是您可以更改的内容,以查看系统将如何对更改做出反应。对于此安全检查点系统,您使用以下参数:

  1. env调度和处理事件的环境对象
  2. num_booths:身份证检查站的数量
  3. check_time: 检查乘客身份证所需的时间
  4. passenger_arrival乘客到达队列的速度

然后,是时候运行模拟了!您可以通过调用env.run()并指定您希望模拟运行多长时间来完成此操作。模拟在几分钟内运行,因此此示例代码将实时运行模拟 10 分钟。

注意:别担心!您无需等待 10 分钟即可完成模拟。因为模拟使您可以虚拟查看实时过程,所以这 10 分钟在计算机上只需几秒钟。

回顾一下,以下是在 Python 中运行模拟的三个步骤:

  1. 建立环境。
  2. 传入参数。
  3. 运行模拟。

但在引擎盖下还有很多事情要做!您需要了解如何选择这些参数,并且必须定义在运行模拟时将调用的所有函数。

让我们开始吧!

如何开始 simpy

在 Python 中创建模拟之前,您应该核对一些待办事项。您需要做的第一件事是确保您对Python 基础知识有充分的了解。特别是,您需要很好地掌握类和生成器。

注意:如果你需要梳洗就这些话题,然后检查了介绍Python中的面向对象编程(OOP)Python简介发电机。这些是模拟过程的关键部分,因此您需要在继续之前了解它们。

您要做的下一件事是安装所需的软件包。您将使用的主要框架是simpy. 这是将创建、管理和运行模拟的核心包。您可以使用以下命令安装它pip

$ python3 -m pip install simpy

您还需要一些内置的 Python 模块。您将使用该statistics模块计算平均等待时间并使用该random模块生成随机数。这些是 Python 标准库的一部分,因此您无需安装任何新东西。

最后,您需要选择运行模拟的方式。通常,您可以选择以下两个选项之一:

  1. 以交互方式运行它:使用Jupyter Notebook,其中每个代码块将包含自己的类或函数定义。输出将显示在笔记本的底部。
  2. 在 shell 中运行它:将您的模拟保存为一个.py文件并告诉 Python 在您的终端中运行它。输出将直接打印到控制台。

选择您最满意的方法!结果应该是一样的。

在本教程中,您将看到对名为simulate.py. 在您学习本教程时,将参考代码块simulate.py以帮助您跟踪所有部分如何组合在一起。供您参考,您可以通过simulate.py以下链接访问完整代码:

随意保存文件simulate.py并在您最喜欢的编辑器中继续操作!

如何使用simpy包进行仿真

运行模拟的第一步simpy是选择要建模的过程。模拟就是创建一个虚拟环境来反映现实世界的系统。本着同样的精神,您将为您的模拟“模拟”一种情况!

想象一下,您受雇帮助当地一家小型电影院的经理。由于等待时间过长,剧院一直收到差评。既关心成本又关心客户满意度的经理只能留住这么多员工。

经理特别担心,一旦那些大片上映,会发生什么混乱:剧院周围排起了长队,员工被拉到了极限,愤怒的观众错过了开场戏……这绝对是一种需要避免的情况!

在检查评论后,经理能够确定去他们剧院的特定电影观众愿意从他们到达到他们坐在座位上的时间最多 10 分钟。换句话说,在剧院过夜的平均等待时间需要为 10 分钟或更短。经理已请求您帮助找出解决方案,使客户的等待时间低于 10 分钟的要求。

头脑风暴模拟算法

在编写一行代码之前,重要的是首先要弄清楚您的流程在现实生活中的运行方式。这是为了确保当您将其传递给机器时,该过程准确反映了客户将真正体验到的内容。以下是您可能会如何思考电影观众可能会采取的步骤来编写您的算法:

  1. 到达剧院,排队,等待购票。
  2. 购买从票房一票。
  3. 排队等候检票。
  4. 获取门票由一个招待员检查。
  5. 选择是否在特许摊位排队:
    • 如果他们排队,那么他们就会购买食物。
    • 如果他们没有排队,那么他们会跳到最后一步。
  6. 找到自己的座位。

对于在剧院售票处购买门票的电影观众来说,这是一个循序渐进的过程。您已经可以看到该流程的哪些部分是可以控制的。您可以通过在售票处增加更多的收银员来影响客户等待的时间。

过程中也有一些部分是无法控制的,比如第一步。您无法控制有多少客户到达,或者他们到达的速度有多快。您可以进行猜测,但您不能简单地选择一个数字,因为这不能很好地反映现实。对于此参数,您可以做的最好的事情是使用可用数据来确定合适的到达时间。

注意:使用历史数据可确保您找到的解决方案将准确反映您在现实生活中可能看到的情况。

考虑到这些因素,是时候构建您的模拟了!

设置环境

在开始构建模拟之前,您需要确保正确配置了开发环境。您要做的第一件事就是导入必要的。您可以通过import在文件顶部声明语句来做到这一点:

import simpy
import random
import statistics

这些是您将用于为剧院经理构建脚本的主要库。请记住,目标是找到平均等待时间少于 10 分钟的最佳员工人数。要做到这一点,您需要收集每个电影观众到达他们的座位所需的时间长度。下一步是声明一个列表来保存这些时间:

wait_times = []

该列表将包含每个电影观众从到达到坐在座位上花费在电影院中的总时间。您可以在文件的最顶部声明​​此列表,以便您可以在稍后定义的任何函数中使用它。

创建环境:类定义

您要构建的模拟的第一部分是系统的蓝图。这将是事情发生的整体环境,人或物体从一个地方移动到另一个地方。请记住,环境可以是许多不同系统之一,例如银行、洗车场或安全检查站。在这种情况下,环境是电影院,因此这将是您的的名称:

class Theater(object):
    def __init__(self):
        # More to come!

现在是时候考虑一​​下电影院的各个部分了。当然,还有剧院本身,这就是您所说的环境。稍后,您将environment使用其中一个simpy函数明确地将剧院声明为实际剧院。现在,env简称它并将其添加到类定义中:

class Theater(object):
    def __init__(self, env):
        self.env = env

好吧,剧院里还有什么?您可以通过仔细考虑您之前计划的模拟算法来解决这个问题。当电影观众到达时,他们需要在售票处排队,收银员将在那里等待帮助他们。现在您已经发现了有关剧院环境的两件事:

  1. 收银员
  2. 电影观众可以从他们那里购买门票

收银员是剧院向顾客提供的一种资源,他们在购票过程中帮助电影观众。现在,您不知道模拟剧院中有多少收银员可用。事实上,这正是您要解决的问题。等待时间如何变化,具体取决于特定晚上工作的收银员数量?

您可以继续调用这个未知变量num_cashiers。此变量将采用的确切值可以稍后整理出来。现在,只要知道它是剧院环境中不可或缺的一部分。将其添加到类定义中:

class Theater(object):
    def __init__(self, env, num_cashiers):
        self.env = env
        self.cashier = simpy.Resource(env, num_cashiers)

在这里,您将新参数添加num_cashiers到您的__init__()定义中。然后,您创建一个资源self.cashier并使用它simpy.Resource()来声明在任何给定时间此环境中可以有多少资源。

注意:在 中simpy资源是环境env中数量有限的部分 ( )。使用其中之一需要时间,并且一次只能使用这么多 ( num_cashiers) 项。

您还需要执行一个步骤。收银员不会自己买票吧?他们要帮助电影观众!同样,您知道购买机票的过程需要一定的时间。但时间是多少?

假设您向经理询问了剧院的历史数据,例如员工绩效评估或购票收据。根据此数据,您了解到在售票处出票平均需要 1 到 2 分钟。你如何simpy模仿这种行为?它只需要一行代码:

yield self.env.timeout(random.randint(1, 3))

env.timeout()告诉simpy在经过一定时间后触发事件。在这种情况下,事件是购买了票。

这需要的时间可能是一分钟、两分钟或三分钟。您希望每个电影观众在收银台上花费不同的时间。为此,您可以random.randint()在给定的低值和高值之间选择一个随机数。然后,对于每个电影观众,模拟将等待选定的时间量。

让我们把它包装在一个 tidy 函数中,并将它添加到类定义中:

class Theater(object):
    def __init__(self, env, num_cashiers):
        self.env = env
        self.cashier = simpy.Resource(env, num_cashiers)

    def purchase_ticket(self, moviegoer):
        yield self.env.timeout(random.randint(1, 3))

在 中启动事件的purchase_ticket()moviegoer,因此它们必须作为必需的参数传递。

注意:您将在下一部分看到电影观众如何实际购买门票!

而已!您已经选择了一个有时限的资源,定义了它的相关流程,并将其编入了您的类定义中。对于本教程,您还需要声明两个资源:

  1. 引导员查票
  2. 服务器卖食品

检查经理发送过来的数据后,您确定服务器需要 1 到 5 分钟的时间来完成订单。此外,引座员查票速度非常快,平均3秒!

您需要将这些资源添加到您的类中并定义相应的函数check_ticket()sell_food(). 你能弄清楚代码应该是什么样子吗?当你有了一个想法时,你可以展开下面的代码块来检查你的理解:

simulate.py显示隐藏

仔细看看新的资源和功能。请注意它们如何遵循与上述相同的格式。sell_food()用于random.randint()生成 1 到 5 分钟之间的随机数,表示电影观众下订单和收到食物所需的时间。

的时间延迟check_ticket()有点不同,因为引导员只需要 3 秒。由于simpy以分钟为单位工作,因此需要将此值作为一分钟的一小部分或3 / 60.

在环境中穿行:功能定义

好的,您已经通过定义一个类来设置环境。您拥有资源和流程。现在你需要一个moviegoer来使用它们。当 amoviegoer到达剧院时,他们将请求资源,等待其过程完成,然后离开。您将创建一个名为 的函数go_to_movies()来跟踪此情况:

def go_to_movies(env, moviegoer, theater):
    # Moviegoer arrives at the theater
    arrival_time = env.now

有三个参数传递给这个函数:

  1. envmoviegoer会受到环境的控制,你会经过这个作为第一个参数。
  2. moviegoer:当每个人在系统中移动时,此变量会对其进行跟踪。
  3. theater此参数使您可以访问您在整个类定义中定义的进程。

您还声明了一个变量arrival_time来保存每个人moviegoer到达剧院的时间。您可以使用对 的simpy调用获得此时间env.now

你会希望每个从你的过程中theater有相应的要求go_to_movies()。例如,类中的第一个进程是purchase_ticket(),它使用cashier资源。他们moviegoer需要向cashier资源发出请求以帮助他们完成整个purchase_ticket()过程。这是一个总结这个的表格:

处理中 theater 请求在 go_to_movies()
purchase_ticket() 请求一个 cashier
check_ticket() 请求一个 usher
sell_food() 请求一个 server

收银员是一个共享资源,这意味着许多电影观众将使用同一个收银员。但是,收银员一次只能帮助一名电影观众,因此您需要在代码中包含一些等待行为。这是它的工作原理:

def go_to_movies(env, moviegoer, theater):
    # Moviegoer arrives at the theater
    arrival_time = env.now

    with theater.cashier.request() as request:
        yield request
        yield env.process(theater.purchase_ticket(moviegoer))

下面是这段代码的工作原理:

  1. theater.cashier.request(): moviegoer生成使用cashier.
  2. yield request: 如果所有当前都在使用中,则moviegoer等待 acashier变为可用。要了解有关该yield关键字的更多信息,请查看如何在 Python 中使用生成器和产量
  3. yield env.process(): moviegoer使用可用cashier来完成给定的过程。在这种情况下,即通过调用 购买机票theater.purchase_ticket()

使用资源后,必须将其释放以供下一个代理使用。您可以使用 显式执行此操作release(),但在上面的代码中,您使用的是with语句。此快捷方式告诉模拟在过程完成后自动释放资源。换句话说,一旦买好票,moviegoer就会离开,收银员会自动准备好迎接下一位顾客。

当收银员腾出时间时,他们moviegoer会花一些时间买票。env.process()告诉模拟转到Theater实例并purchase_ticket()在此moviegoer. 该moviegoer会重复这一要求,使用,发布周期有自己的票检查:

def go_to_movies(env, moviegoer, theater):
    # Moviegoer arrives at the theater
    arrival_time = env.now

    with theater.cashier.request() as request:
        yield request
        yield env.process(theater.purchase_ticket(moviegoer))

    with theater.usher.request() as request:
        yield request
        yield env.process(theater.check_ticket(moviegoer))

这里,代码的结构是相同的。

然后,还有从特许摊位购买食物的可选步骤。您无法知道电影观众是否愿意购买零食和饮料。处理这种不确定性的一种方法是在函数中引入一些随机性

每个人moviegoer都愿意或不愿意购买食物,您可以将其存储为布尔值 TrueFalse. 然后,使用random模块让模拟随机决定这个特定的人moviegoer是否要去特许摊位:

def go_to_movies(env, moviegoer, theater):
    # Moviegoer arrives at the theater
    arrival_time = env.now

    with theater.cashier.request() as request:
        yield request
        yield env.process(theater.purchase_ticket(moviegoer))

    with theater.usher.request() as request:
        yield request
        yield env.process(theater.check_ticket(moviegoer))

    if random.choice([True, False]):
        with theater.server.request() as request:
            yield request
            yield env.process(theater.sell_food(moviegoer))

条件语句将返回以下两种结果之一:

  1. Truemoviegoer将请求服务器和秩序的食物。
  2. Falsemoviegoer会,而不是去寻找自己的座位上,而无需购买任何零食。

现在,请记住此模拟的目标是确定应在员工中的收银员、引座员和服务员的数量,以将等待时间保持在 10 分钟以内。要做到这一点,您需要知道任何人moviegoer到达他们的座位需要多长时间。您env.now在函数的开头使用跟踪arrival_time,并在每个moviegoer完成所有过程并进入剧院时再次使用:

def go_to_movies(env, moviegoer, theater):
    # Moviegoer arrives at the theater
    arrival_time = env.now

    with theater.cashier.request() as request:
        yield request
        yield env.process(theater.purchase_ticket(moviegoer))

    with theater.usher.request() as request:
        yield request
        yield env.process(theater.check_ticket(moviegoer))

    if random.choice([True, False]):
        with theater.server.request() as request:
            yield request
            yield env.process(theater.sell_food(moviegoer))

    # Moviegoer heads into the theater
    wait_times.append(env.now - arrival_time)

env.now用来获取moviegoer完成所有流程并到达座位的时间。您arrival_time从该出发时间中减去观影者的时间,并将由此产生的时间差附加到您的等候名单中wait_times

注意:您可以将出发时间存储在一个单独的变量中,例如departure_time,但这会使您的代码非常重复,这违反了DRY 原则

moviegoer是准备观看一些预览!

让事情发生:功能定义

现在您需要定义一个函数来运行模拟。run_theater()将负责创建剧院的实例并生成电影观众,直到模拟停止。这个函数应该做的第一件事是创建一个剧院的实例:

def run_theater(env, num_cashiers, num_servers, num_ushers):
    theater = Theater(env, num_cashiers, num_servers, num_ushers)

由于这是主要过程,因此您需要传递迄今为止声明的所有未知数:

  • num_cashiers
  • num_servers
  • num_ushers

这些都是模拟创建和控制环境所需的变量,因此将它们全部传递是绝对重要的。然后,您定义一个变量theater并告诉模拟设置具有一定数量的收银员、服务员和招待员的剧院。

您可能还想以一些在剧院等候的电影观众来开始您的模拟。估计一开门就有几个人准备走了!经理说,预计票房一开放,大约有 3 名观众排队准备买票。您可以告诉模拟继续并通过这个初始组,如下所示:

def run_theater(env, num_cashiers, num_servers, num_ushers):
    theater = Theater(env, num_cashiers, num_servers, num_ushers)

    for moviegoer in range(3):
        env.process(go_to_movies(env, moviegoer, theater))

range()过去常常让 3 位电影观众在剧院里演出。然后,您使用env.process()告诉模拟准备移动它们穿过剧院。其余的电影观众将在他们自己的时间进入剧院。因此,只要模拟正在运行,该功能就应该不断地将新客户送入影院。

您不知道新观众需要多长时间才能进入影院,因此您决定查看过去的数据。使用带有时间戳的票房收据,您可以了解到电影观众平均每 12 秒就会到达剧院一次。现在你要做的就是告诉函数在生成一个新人之前等待这么长时间:

def run_theater(env, num_cashiers, num_servers, num_ushers):
    theater = Theater(env, num_cashiers, num_servers, num_ushers)

    for moviegoer in range(3):
        env.process(go_to_movies(env, moviegoer, theater))

    while True:
        yield env.timeout(0.20)  # Wait a bit before generating a new person

        # Almost done!...

请注意,您使用十进制数0.20来表示 12 秒。要获得这个数字,您只需将 12 秒除以 60 秒,即一分钟的秒数。

等待后,该函数应递增moviegoer1 并生成下一个人。该发电机的功能是您用来初始化第3名影迷同一个:

def run_theater(env, num_cashiers, num_servers, num_ushers):
    theater = Theater(env, num_cashiers, num_servers, num_ushers)

    for moviegoer in range(3):
        env.process(go_to_movies(env, moviegoer, theater))

    while True:
        yield env.timeout(0.20)  # Wait a bit before generating a new person

        moviegoer += 1
        env.process(go_to_movies(env, moviegoer, theater))

而已!当您调用此函数时,模拟将生成 3 个电影观众开始并使用 开始在影院中移动他们go_to_movies()。之后,新的电影观众将以12秒的间隔到达剧院,并在他们自己的时间穿过剧院。

计算等待时间:函数定义

此时,您应该有一个列表wait_times,其中包含每个电影观众到达他们的座位所花费的总时间。现在您需要定义一个函数来帮助计算moviegoer从他们到达到他们完成检查他们的票所花费的平均时间。get_average_wait_time()只是这样做:

def get_average_wait_time(wait_times):
    average_wait = statistics.mean(wait_times)

此函数将您的wait_times列表作为参数并用于statistics.mean()计算平均等待时间。

由于您正在创建将由电影院经理使用的脚本,因此您需要确保用户可以轻松读取输出。您可以添加一个调用函数calculate_wait_time()来执行此操作:

def calculate_wait_time(arrival_times, departure_times):
    average_wait = statistics.mean(wait_times)
    # Pretty print the results
    minutes, frac_minutes = divmod(average_wait, 1)
    seconds = frac_minutes * 60
    return round(minutes), round(seconds)

函数的最后一部分用于divmod()以分钟和秒为单位返回结果,因此管理人员可以轻松了解程序的输出。

选择参数:用户输入函数定义

在构建这些函数时,您遇到了一些尚未明确定义的变量:

  • num_cashiers
  • num_servers
  • num_ushers

这些变量是您可以更改以查看模拟如何变化的参数。如果一部大片有顾客在街区周围排队,那么应该有多少收银员在工作?如果人们在票房上飞驰而过却陷入让步怎么办?什么值num_servers将有助于缓解流程?

注意:这就是模拟的美妙之处。它允许您尝试这些事情,以便您可以确定现实生活中的最佳决策。

使用您的模拟的任何人都需要能够更改这些参数的值以尝试不同的场景。为此,您将创建一个辅助函数来从用户那里获取这些值:

def get_user_input():
    num_cashiers = input("Input # of cashiers working: ")
    num_servers = input("Input # of servers working: ")
    num_ushers = input("Input # of ushers working: ")
    params = [num_cashiers, num_servers, num_ushers]
    if all(str(i).isdigit() for i in params):  # Check input is valid
        params = [int(x) for x in params]
    else:
        print(
            "Could not parse input. The simulation will use default values:",
            "\n1 cashier, 1 server, 1 usher.",
        )
        params = [1, 1, 1]
    return params

这个函数只是调用 Python 的input()函数来检索用户的数据。由于用户输入存在混乱的风险,您可以包含一个if/else子句来捕获任何无效的内容。如果用户输入错误数据,则模拟将以默认值运行。

完成设置:主要功能定义

您要创建的最后一个函数是main(). 这将确保您的脚本在命令行上执行时以正确的顺序运行。您可以阅读在 Pythonmain()定义主要函数中的更多信息。这是你main()应该是什么样子:

def main():
  # Setup
  random.seed(42)
  num_cashiers, num_servers, num_ushers = get_user_input()

  # Run the simulation
  env = simpy.Environment()
  env.process(run_theater(env, num_cashiers, num_servers, num_ushers))
  env.run(until=90)

  # View the results
  mins, secs = get_average_wait_time(wait_times)
  print(
      "Running simulation...",
      f"\nThe average wait time is {mins} minutes and {secs} seconds.",
  )

以下是main()工作原理:

  1. 通过声明一个随机种子来设置您的环境。这可确保您的输出看起来像您在本教程中看到的那样。
  2. 向程序的用户查询一些输入。
  3. 创建环境并将其保存为变量env,这将在每个时间步长中移动模拟。
  4. 告诉 simpy运行进程run_theater(),它创建剧院环境并生成电影观众来通过它。
  5. 确定您希望模拟运行多长时间。作为默认值,模拟设置为运行 90 分钟。
  6. 将 的输出存储get_average_wait_time()在两个变量中,minssecs
  7. 用于 print()向用户显示结果。

至此,设置完成!

如何运行模拟

只需再多写几行代码,您就可以亲眼目睹您的模拟变得栩栩如生。但首先,这里是您迄今为止定义的函数和类的概述:

  • Theater这个类定义用作您要模拟的环境的蓝图。它确定有关该环境的一些信息,例如可用的资源类型以及与它们相关联的进程。

  • go_to_movies()该函数明确请求使用资源,经过关联的过程,然后将其释放给下一个电影观众。

  • run_theater()此功能控制模拟。它使用Theater类蓝图创建剧院的实例,然后调用go_to_movies()生成并移动人们穿过剧院。

  • get_average_wait_time()此功能可计算出moviegoer通过剧院所需的平均时间。

  • calculate_wait_time()此功能可确保最终输出易于用户阅读。

  • get_user_input()这个函数允许用户定义一些参数,比如有多少可用的收银员。

  • main()此功能可确保您的脚本在命令行中正常运行。

现在,您只需要另外两行代码来调用您的 main 函数:

if __name__ == '__main__':
    main()

这样,您的脚本就可以运行了!打开您的终端,导航到您存储的位置simulate.py,然后运行以下命令:

$ python simulate.py
Input # of cashiers working:

系统将提示您选择仿真所需的参数。以下是使用默认参数的输出:

$ python simulate.py
Input # of cashiers working: 1
Input # of servers working: 1
Input # of ushers working: 1
Running simulation...
The average wait time is 42 minutes and 53 seconds.

哇!等了好久啊!

何时改变现状

请记住,您的目标是向经理提供解决方案,了解他需要多少员工才能将等待时间保持在 10 分钟以内。为此,您需要调整参数以查看哪些数字提供了最佳解决方案。

首先,尝试一些完全疯狂的事情并最大限度地利用资源!假设有 100 个收银员、100 个服务器和 100 个引导员在这个剧院工作。当然,这是不可能的,但是使用高得离谱的数字会很快告诉您系统的限制是多少。现在就试试:

$ python simulate.py
Input # of cashiers working: 100
Input # of servers working: 100
Input # of ushers working: 100
Running simulation...
The average wait time is 3 minutes and 29 seconds.

即使您最大限度地利用了资源,您的等待时间也只能缩短到 3 分半钟。现在尝试更改数字,看看是否可以像经理要求的那样将等待时间缩短到 10 分钟。你想出了什么解决方案?您可以展开下面的代码块以查看一种可能的解决方案:

simulate.py显示隐藏

此时,您将向经理展示您的结果并提出建议以帮助改进剧院。例如,为了降低成本,他可能希望在剧院前面安装 10 个售票亭,而不是每晚都安排 10 个收银员。

结论

在本教程中,您学习了如何使用该框架在 Python 中构建和运行模拟simpy。您已经了解系统如何让代理进行处理,以及如何创建这些系统的虚拟表示以加强它们免受拥塞和延迟的影响。虽然模拟的类型可能会有所不同,但整体执行是相同的!您将能够将在这里学到的知识应用到各种不同的场景中。

现在你可以:

  • 逐步头脑风暴模拟算法
  • 在 Python 中创建一个虚拟环境simpy
  • 定义代表代理和进程的函数
  • 更改模拟参数以找到最佳解决方案

你可以用它做很多事情simpy,所以不要让你的探索就此停止。将您学到的知识应用到新的场景中。您的解决方案可以帮助人们节省宝贵的时间和金钱,所以深入了解您可以优化的其他流程!您可以通过单击下面的链接下载您在本教程中构建的脚本的源代码:

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。