4.4 离散事件仿真
在仿真中,事件需要随机生成并安排时间,因此Lua中的随机数和协程功能已经得到改进。
随机数生成
随机数的概率分布已得到扩展,但传统用法仍然保留。
math.randomseed (x, [, disttab])
根据整数参数x和可选的disttab表返回一个种子。 disttab中的选项包括:
- dist:概率分布,可选值为"normal"、"exponential"、"poisson"和"uniform"。默认为"uniform"。
- mu:均值或期望值,默认为1。
- sigma:标准差(仅适用于正态分布),默认为1。
当无参数调用时,返回区间[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.resume或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翻译,如有遗漏请反馈。