SimPy模拟顾客去超市购买商品的过程

场景介绍:

  1. 假设有500名顾客,顾客到达超市的时间间隔服从泊松分布, lambd设为1,也就是平均每隔1分钟就会
    有一名顾客到达超市。
  2. 每位顾客在超市挑选商品的时间服从指数分布,lambd设为1/Time,Time=4分钟。
  3. 超市有三个结账窗口,当三个结账窗口都在进行工作时,顾客需要排队等候,当窗口空闲时,排在队
    列最前面的顾客可以去结账,每名顾客结账时间为30秒。
  4. 顾客结完账离开超市。统计500名顾客在超市花费的平均时间(提示,记录顾客到达超市和离开超市
    的两个时间点,这段时间差即为该顾客在超市花费的时间)。

一、simpy是什么

SimPy是基于标准Python语言的基于过程的离散事件仿真框架。SimPy中的进程由Python的generator函数定义,例如,可以用于对客户、车辆或智能体等活动组件进行建模。SimPy还提供各种类型的共享资源来模拟有限容量的拥塞点(如服务、收银台和通道)。

二、基本概念

SimPY使用Environment,Process,Event,Resource四大概念来进行离散事件的仿真。

①Environment就是整体仿真所在的时间,主要用于提取时间。

②Process就是仿真过程中的实体,如:顾客, 设备, 车辆等。 Process本质上也是一个event。源代码里面可以看到是继承Event的一个类。

SimPy进程(由Process或env.Process()创建)也具有作为事件的良好属性。这意味着,一个过程可以产生另一个过程。当另一个进程结束时,它将被恢复。事件的值将是该进程的返回值:

③Event是仿真中触发的事件,可以理解为一个定时器。当定时器到时时,触发事件。

④Resource是仿真中的资源,如ATM机,服务器等。

活动组件(顾客)的行为由进程(Processes)进行建模。所有进程都存在于同个环境中。它们通过事件环境相互作用。

进程由简单的Python生成器描述。您可以通过function或method调用它,这取决于它是一个普通函数还是一个类的方法。进程在生命周期中可以创造并挂起(yield)事件(Events),等待事件的触发。

当进程生成事件时,该进程将被挂起。当事件发生时(事件已触发),SimPy恢复进程。多个进程可以等待同一事件。SimPy按照生成事件的统一顺序进行恢复。

一个重要的事件类型是超时(Timeout)。此类事件在经过一定仿真时间后被触发。它们允许进程在给定的时间内睡眠(或保持其状态)。可以通过调用进程所在环境的适当方法(例如Environment.Timeout())来创建超时事件和所有其他事件。

三、仿真环境

Environment 决定仿真的起点/终点, 管理仿真元素之间的关联, 主要 API 有

simpy.Environment.process - 添加仿真进程
simpy.Environment.event - 创建事件
simpy.Environment.timeout - 提供延时(timeout)事件
simpy.Environment.until - 仿真结束的条件(时间或事件)
simpy.Environment.run - 仿真启动

四、顾客进程

def Client(env, name, sm):
    """
    客户到达超市,挑选商品,结账流程,结束后离开
    """
    time_start=env.now #顾客到达超市的时间点
    print('%s 到达超市 at %.2f.' % (name, env.now))
    yield env.timeout(expon.rvs(scale=4))  # 假设挑选时间  指数分布
    print('%s 挑选商品完成 at %.2f.' % (name, env.now))
    timeChoose=env.now-time_start
    with sm.checkstand.request() as request: 
        yield request
        timeWait=env.now-time_start-timeChoose
        print('%s 开始结账   at %.2f.' % (name, env.now))
        yield env.process(sm.serve(name))
        print('%s 结账离开超市 at %.2f.' % (name, env.now))
        time_end = env.now #顾客离开超市的时间点
        time_arr.append(time_end-time_start)  #加入总时间数组
        chooseTime.append(timeChoose) #挑选时间
        waitTime.append(timeWait)   #等待时间

顾客的进程需要引用环境(env)来创建新的事件

五、共享资源——SimPy的Resource类

资源(resource)的request()方法生成一个事件(event), 该事件会等待直至资源可用。您将“拥有”资源,直至资源被释放。

在代码中使用with构造资源的请求使用,资源在使用后将自动释放。如果没通过with使用request(),您需要自行使用release()释放资源。

当您释放资源后,下一个等待的进程将激活,“占用”资源。资源的排队规则遵循先进先出(first in—first out,FIFO)策略。

创建资源: self.checkstand = simpy.Resource(env, NUM_CHECKSTANDS)
超市进程:

class SuperMarket(object):
    """
    一个超市,拥有指定数目的收银台。
    """

    def __init__(self, env, NUM_CHECKSTANDS):
        self.env = env
        self.checkstand = simpy.Resource(env, NUM_CHECKSTANDS)  # 创建资源


    def serve(self, client):
        """结账时间固定为30秒(0.5分钟),使用一个顾客进程"""
        print("收银台开始为%s提供服务" % client)
        yield self.env.timeout(SERVICE_TIME)  # 假设服务时间0.5分钟

顾客等待资源:

with sm.checkstand.request() as request:
    yield request

六、主进程:

def setup(env, NUM_CHECKSTANDS,CLIENT_NUMBER):
    """创建一个超市,有三个结账窗口"""

    # 创建超市
    supermarket = SuperMarket(env, NUM_CHECKSTANDS)

    i=0 #客户数目

    # 在仿真过程中持续创建客户(直至500)
    while True:
        yield env.timeout(poisson.rvs(mu=1))  # 顾客到达超市的时间间隔服从泊松分布, lambd设为1
        i += 1
        env.process(Client(env, '顾客【%d】' % i, supermarket))  # 创建顾客进程,还有其交互的超市进程
        if i==CLIENT_NUMBER :
            break

七、程序运行结果:

SimPy模拟顾客去超市购买商品的过程_第1张图片
可以发现,实际上等待的时间是很少的

你可能感兴趣的:(Python)