4.4 离散事件仿真

在仿真中,事件需要随机生成并安排时间,因此Lua中的随机数和协程功能已经得到改进。

随机数生成

随机数的概率分布已得到扩展,但传统用法仍然保留。

math.randomseed (x, [, disttab])

根据整数参数x和可选的disttab表返回一个种子。 disttab中的选项包括:

  • dist:概率分布,可选值为"normal"、"exponential"、"poisson"和"uniform"。默认为"uniform"。
  • mu:均值或期望值,默认为1。
  • sigma:标准差(仅适用于正态分布),默认为1。

math.random ([seed|m [, n]])

当无参数调用时,返回区间[0, 1)内的均匀实数。当用整数 m 调用时,返回区间[1, m]内的均匀整数。当用两个整数 m 和 n 调用时,返回区间[m, n]内的均匀整数。
当使用自math.randomseed生成的seed调用时,返回遵循种子指定的分布的数值。此用法可以重写为seed:random()。示例如下:

local seed = math.randomseed(1, {dist = "normal", mu = 5, sigma = 3})  -- 为正态分布随机数生成器设置一个种子
for i = 1, 10 do
    print(seed:random())                                               -- 打印一个随机数
end

事件调度

在离散事件仿真中,事件需要按照时间顺序安排。在 MicroCity Web 中,事件定义为 Lua 的函数或协程,因此协程库中添加了三个新成员。

coroutine.queue(rt [, f|co, arg1, ···])

将当前事件(函数或协程)或另一个事件(函数f或协程co)在相对时间rt(>=0)后排队以供以后执行。参数arg1,...将作为参数传递给主体函数。排队的事件(协程)可以在脚本结束时按时间顺序隐式恢复,也可以通过使用coroutine.resumecoroutine.qexec来显式恢复。

coroutine.qtime()

返回当前仿真时间(排队时间)。

coroutine.qexec()

按时间顺序执行所有事件。默认情况下,MicroCity Web将在脚本结束时隐式运行此函数,但用户在必要时可以显式调用它。

模拟 M/M/1 队列

为了演示这些功能,这里使用 M/M/1 队列 作为示例:

一个排队系统平均每3分钟为一位顾客服务,而服务器平均每2.5分钟为一位顾客提供服务。先到先服务。可以在此处绘制一个 事件关系图

以及源代码:

local M = "Idle"                                                            --初始化服务器状态
local Q = 0                                                                 --初始化队列长度
local arrival_time = math.randomseed(1, {dist = "exponential", mu = 3})     --到达时间随机种子
local service_time = math.randomseed(1, {dist = "exponential", mu = 2.5})   --服务时间随机种子

function Arrive()                                                           --顾客到达
    Q = Q + 1                                                               --队列长度加一
    local t = coroutine.qtime()                                             --获取当前仿真时间

    if t < 100 then                                                         --当当前时间小于100时
        local ta = arrival_time:random()                                    --获取下一个到达时间
        coroutine.queue(ta, Arrive)                                         --安排下一个到达事件
    end

    if M == "Idle" then                                                     --检查服务器状态
        coroutine.queue(0, Start)                                           --开始提供服务
    end

    print("顾客在时间 ", t, " 到达,队列长度为 ", Q)                          
end

function Start()                                                            --开始提供服务
    M = "Busy"                                                              --将服务器状态设置为忙碌
    Q = Q - 1                                                               --队列长度减一

    local ts = service_time:random()                                        --获取一个服务时间
    coroutine.queue(ts, Leave)                                              --安排离开事件

    print("服务开始时间: ", coroutine.qtime(), " Q = ", Q)
end

function Leave()                                                            --顾客离开
    M = "Idle"                                                              --将服务器设置为空闲状态

    if Q > 0 then                                                           --如果还有顾客在队列中
        coroutine.queue(0, Start)                                           --开始为下一位顾客提供服务
    end

    print("顾客离开时间: ", coroutine.qtime(), " Q = ", Q)    
end

coroutine.queue(0, Arrive)                                                  --安排第一位顾客到达的事件

本文使用ChatGPT翻译,如有遗漏请反馈open in new window

Last Updated:
Contributors: huuhghhgyg