Python|数学建模|排队论仿真

2021-11-22 11:09:13 浏览数 (1)

数模准备过程中,写了这种运筹学仿真的代码,虽然自己选了C题没有用到,但考虑到市面上存在的仿真代码较少,聊以为分享。

文档介绍

本文档使用了Python的离散事件仿真库对于排队论模型进行了仿真

仿真的主要目的是提供个性化定制,如对分布的设定,对排队规则的设定等。通过蒙特卡洛模拟得到复杂规则下难以得到的数值解。

本文档提供了:

基础排队模型仿真

Erlang分布实现

通用分布函数适配器


工具库

库依赖

代码语言:javascript复制
from numpy.random import *
from simpy import *

高阶函数随机数生成器

输入:分布函数,单一参数(多参数请柯里化) 输出:随机数

代码语言:javascript复制
def rng(dis,param):
    """random number generator"""
    def generate():
        return dis(lam=param,size=1)[0]
    return generate

Erlang分布函数:

输入:阶数

输出:k阶erlang分布函数

代码语言:javascript复制
def erlang(k):
    """由k个指数分布拟合"""
    def exp2erlang(lam,size):
        res=[]
        for n in range(size):
            k_poisson= exponential(lam/k,size=k)
            sum=0
            for x in k_poisson:
                sum = sum   x
            res.append(sum)
        return res
    return exp2erlang

测试,计算分布期望

代码语言:javascript复制
x=rng(erlang(10),10)
sum=0
for i in range(10000):
    sum= sum x()
print(sum/10000)

结果为9.99565983119657,说明函数正确

FIFO队列模型

代码语言:javascript复制
#典型银行模型:FIFO
def bankSample(X,Y,Z,A,B,EX):
    """
银行排队服务例子

情景:
  一个柜台对客户进行服务, 服务耗时, 客户等候过长会离开柜台
    %X 表示时间间隔分布
    %Y 表示服务时间的分布
    %Z 表示服务台的个数
    %A 表示系统的容量,此处特殊化为客户的耐心时间分布
    %B 表示顾客数
    %以上参数必须有界,受到计算机精度限制,可以使用大常数近似无穷
    %C 表示服务规则,请修改函数
    %EX 传递了银行储蓄额的分布
  """
    #加入随机种子是为了对比模型的变化
    seed(2)
    def source(env, number, interval,counter):
        """生成客户"""
        for i in range(number):
            c = customer(env, '客户d' % i, counter, time_in_bank=Y(),account=EX())
            env.process(c)
            yield env.timeout(interval)
    #成功服务的客户
    SUCC=0
    #成功客户等待时间
    WAIT=0
    #成功客户逗留时间
    STAY=0
    #业务额
    AMT=0
    def customer(env, name, counter, time_in_bank,account):
        nonlocal WAIT
        nonlocal SUCC
        nonlocal STAY
        nonlocal AMT
        """顾客服务与离开仿真"""
        arrive = env.now
        #print('%7.4f  %s: 到达' % (arrive, name))
        with counter.request() as req:
            patience = A()
            # 直到到达或者失去耐心
            results = yield req | env.timeout(patience)
            wait = env.now - arrive
        
            if req in results:
                # 到达
                WAIT=WAIT wait
                STAY=STAY time_in_bank
                AMT= AMT   account
                #print('%7.4f %s:等待%6.3f' % (env.now, name, wait))
                yield env.timeout(time_in_bank)
                SUCC=SUCC 1
                #print('%7.4f %s:服务完成' % (env.now, name))
            else:
                # We reneged
                pass
                #print('%7.4f %s:等待%6.3f后离开' % (env.now, name, wait))
    # 初始化环境
    print('排队问题仿真')
    env = Environment()

    # 开始协程
    counter = Resource(env, capacity=Z)
    env.process(source(env, B, X(), counter))
    env.run()
    print("总服务人数:{0:n}人".format(SUCC))
    print("总营业额:{0:n}元".format(AMT))
    print("总计失去: {0:n}名客户".format(B-SUCC))
    print("损失率为: {0:n}%".format((B-SUCC)/B*100))
    print("平均等待时间:{0:n}".format(WAIT/SUCC) )
    print("平均耗费时间:{0:n}".format(STAY/SUCC) )

我们以下列参数作为输入

代码语言:javascript复制
#间隔分布
X=rng(erlang(3),3)
#服务时间分布
Y=rng(erlang(3),10)
#耐心时间分布
A=rng(erlang(3),3)
#业务额分布正态
def normaltocurry(s):
    def normalcurry(lam,size):
        return normal(lam,s,size=size)
    return normalcurry
EX=rng(normaltocurry(200),1000)
bankSample(X,Y,3,A,1000,EX)

输出的结果为:

排队问题仿真

总服务人数:417人

总营业额:409903元

总计失去: 583名客户

损失率为: 58.3%

平均等待时间:1.8828

平均耗费时间:10.0787

营业额优先队列模型

然后,我们转变一下模型,变成营业额越高越优先的队列

代码语言:javascript复制
#银行模型·ELite:优先队列
def eliteBankSample(X,Y,Z,A,B,EX):
    """
银行排队服务例子

情景:
  一个柜台对客户进行服务, 服务耗时, 客户等候过长会离开柜台
    %X 表示时间间隔分布
    %Y 表示服务时间的分布
    %Z 表示服务台的个数
    %A 表示系统的容量,此处特殊化为客户的耐心时间分布
    %B 表示顾客数
    %以上参数必须有界,受到计算机精度限制,可以使用大常数近似无穷
    %C 表示服务规则,请修改函数
    %EX 传递了银行储蓄额的分布
  """
    #加入随机种子是为了对比模型的变化
    seed(2)
    def source(env, number, interval,counter):
        """生成客户"""
        for i in range(number):
            c = customer(env, '客户d' % i, counter, time_in_bank=Y(),account=EX())
            env.process(c)
            yield env.timeout(interval)
    #成功服务的客户
    SUCC=0
    #成功客户等待时间
    WAIT=0
    #成功客户逗留时间
    STAY=0
    #业务额
    AMT=0
    def customer(env, name, counter, time_in_bank,account):
        nonlocal WAIT
        nonlocal SUCC
        nonlocal STAY
        nonlocal AMT
        """顾客服务与离开仿真"""
        arrive = env.now
        #print('%7.4f  %s: 到达' % (arrive, name))
        #以业绩作为优先级,priority越小,优先级越大,
        with counter.request(priority = 1/account) as req:
            patience = A()
            # 直到到达或者失去耐心
            results = yield req | env.timeout(patience)
            wait = env.now - arrive
        
            if req in results:
                # 到达
                WAIT=WAIT wait
                STAY=STAY time_in_bank
                AMT= AMT   account
                #print('%7.4f %s:等待%6.3f' % (env.now, name, wait))
                yield env.timeout(time_in_bank)
                SUCC=SUCC 1
                #print('%7.4f %s:服务完成' % (env.now, name))
            else:
                # We reneged
                pass
                #print('%7.4f %s:等待%6.3f后离开' % (env.now, name, wait))
    # 初始化环境
    print('排队问题仿真')
    env = Environment()

    # 开始协程
    counter = PriorityResource(env, capacity=Z)
    env.process(source(env, B, X(), counter))
    env.run()
    print("总服务人数:{0:n}人".format(SUCC))
    print("总营业额:{0:n}元".format(AMT))
    print("总计失去: {0:n}名客户".format(B-SUCC))
    print("损失率为: {0:n}%".format((B-SUCC)/B*100))
    print("平均等待时间:{0:n}".format(WAIT/SUCC) )
    print("平均耗费时间:{0:n}".format(STAY/SUCC) )

以相同的参数测试

结果为:

排队问题仿真 总服务人数:432人 总营业额:450145元 总计失去: 568名客户 损失率为: 56.8% 平均等待时间:1.28862 平均耗费时间:9.69799

0 人点赞