简易模拟系统



引言


仿真是一种进行实验的方法,将所研究的实际系统替换为模型。 在这样的模型中,您可能会丢失个别情况,也可能会丢失许多情况。 收集的统计信息可以帮助得出有关系统中的过程的结论,以及概述优化路径。

仿真建模通常被认为是一种实验测试,但同时成本较低,它允许您快速更改参数并动态观察仿真系统。

在模拟建模中已经使用了大约半个世纪,已经使用了计算机模型。 为了开发它们,已经创建了许多不同的程序和框架,在我看来,其中最成熟的是用于建模排队系统(QS)的工具。 可以在链接[1][2]上找到更详细的QS仿真程序之一-GPSS World(通用仿真系统-通用建模系统)。

该程序的概念是Go仿真框架的基础。

GPSS中的仿真


GPSS中的模型是一系列块(命令),描述了交易在其间移动的模拟对象。 当事务进入该块时,将生成事件,这些事件导致建模对象的状态发生变化或导致事务的状态/参数发生变化。

十个顺序的主要块:GENERATE,TERMINATE,ASSIGN,SEIZE,RELEASE,QUEUE,ADVANCE,DEPART,START。 总共约有三打。 块具有参数,可以是数字,函数名称,仿真程序中的标签,变量名称。 例如,可以在此处找到有关块的更多详细信息。

GPSS中的对象具有一组标准数字属性(NAV)和标准逻辑属性(ALS)。 例如,对于队列,NAV之一是当前长度,而某些设备的ALS示例将为TRUE或忙碌(FALSE)。
在某些版本的GPSS中,可以看到建模过程的可视化,但是通常不存在。 根据仿真结果,在GPSS中生成报告,指示所有对象的NAV和ALS。

实施


Go中的实现是开发一组功能类似于GPSS块的对象。 首先创建了Pipeline-在其中执行模拟的对象。

基于包含描述模拟系统的组件列表的map 。 由于在仿真过程中,交易必须严格按照顺序进行,因此在添加Append组件的方法中,实现了同时添加交易目的地指示的添加过程。 组件的名称用作map键,因此每个组件必须具有唯一的名称。

添加所有组件后,可以使用Start方法启动仿真。 在其内部,在给定的仿真时间内实现了所有组件的循环旁路。 在模拟结束时,您可以打印包含NAV和ALS的报告。

第二个重要元素是描述模拟的实际组件。 实现了:生成器-生成事务,高级-在事务路径上创建延迟,队列-事务队列,设备-事务仅捕获一段时间的设备,漏洞-事务在路径末尾失败的“漏洞”。 当然,这样的集合不足以创建复杂的仿真模型,但足以制定出解决方案并与GPSS结果进行比较。 所有组件均实现IBaseObj接口,该接口涵盖了所需的最低功能。

每个组件都有一个事务队列。 直接作为队列,它仅在队列中使用,对于其他组件,它只是一个存储库。 该队列基于map实现。 在建模过程中,组件依次通过并检查事务的准备情况(满足特定条件)以传输到下一个组件。 传输通过下一个组件的AppendTransact方法执行。 如果传输成功,则将事务从队列中分别删除,下一个组件依次进行处理。 由于为每个组件定义了多个收件人,因此,如果不可能将交易发送给一个收件人,我们将尝试将其发送给另一个收件人。

为了在确定事务出现的时间和创建延迟时生成随机变量,使用了Go中的PRNG函数。

由于同时进行建模时,在不同的组件之间可能会有许多事务在移动,因此出现了在组件内部使用goroutine的想法。 管道通过组件,为每个组件启动HandleTransacts处理程序,并在其中创建goroutine。 在所有goroutine完成之后,模型时间计数器将递增,并HandleTransacts调用HandleTransacts

最后一个关键对象是事务本身。 他具有一个标识符,一个出生和死亡的时间,一个所有者(他现在在其中),用于计算SCA和SCHL的许多参数。

在图。 图1是建模过程中框架主要对象相互作用的结构图。


1.仿真中主要对象相互作用的通用结构图

模拟实例


假设您需要模拟美发师的工作。 这是GPSS的一个著名例子。 参观者随机走动,频率为18±6分钟,他们的人数事先未知。 我们有一个理发师,他花了16±4分钟进行理发。 那么,他将削减多少人工作一天? 有多少人排队? 理发平均需要多少时间,人们排队等候多少时间? 很多问题和简单的模拟。 图中的框图 2。


2.美发师建模的结构方案

用于构建模型的代码如下。

 barbershop := NewPipeline("Barbershop", true) //   clients := NewGenerator("Clients", 18, 6, 0, 0, nil) //   chairs := NewQueue("Chairs") //  master := NewFacility("Master", 16, 4) //  hole := NewHole("Out") //  barbershop.Append(clients, chairs) //       barbershop.Append(chairs, master) //      barbershop.Append(master, hole) //     barbershop.Append(hole) //        barbershop.Start(480) //    <-barbershop.Done //   barbershop.PrintReport() 

仿真结果可以在这里找到。
管道名称“理发店”
模拟时间480
对象名称“椅子”
内容上限1
条目总数26
零词条11
允许零条目42.31%
在队列0中
平均时间/跨2.58
平均时间/跨度(无零条目)4.47

对象名称“客户”
产生26

对象名称“主”
平均前进16.46
平均利用率89.17
号码输入26.00
设施内处理26

对象名称“ Out”
杀死25
平均前进16.56
平均寿命19.44

服务了25个客户,在模拟完成时的第26个仍是主持人。 队列不超过1人,没有等待11人(零通过),立即去理发。 平均而言,人们在队列中花费了2.58分钟,而在等待中(而不是零通过)的人中,只有4.47分钟。 89.17%的时间是个理发师。

当然,如果您进行其他模拟,结果将会改变。 但是在一系列模拟过程中,向导负载的总体水平和服务的客户数量将可见。 GPSS中的类似模拟会产生类似的结果。

另一个例子。 有一个办公室,有10名员工和一个厕所。 人们想每隔90±60分钟去厕所一次,上厕所5±3分钟,上厕所15±10分钟,回到办公室5±3分钟。 在图5中,我们将进行9个小时的仿真(8个小时的工作时间+ 1个小时的午餐时间)。 图3是结构图。


3.厕所使用模型的结构图:左边有一个厕所,右边有两个厕所

左边是带一个厕所的模型,右边是带两个厕所的模型。 以下是模型代码。

 waterclosetsim := NewPipeline("Water Closet Simulation", false) office := NewGenerator("Office", 0, 0, 0, 10, nil) wantToToilet:= NewAdvance("Wanted to use the toilet", 90, 60) pathToWC := NewAdvance("Path to WC", 5, 3) queue := NewQueue("Queue to the WC") pathFromWC := NewAdvance("Path from WC", 5, 3) wc := NewFacility("WC", 15, 10) pathToOffice:= NewAdvance("Path from WC", 5, 3) waterclosetsim.Append(office, wantToToilet) waterclosetsim.Append(wantToToilet, pathToWC) waterclosetsim.Append(pathToWC, queue) waterclosetsim.Append(queue, wc) waterclosetsim.Append(wc, pathFromWC) waterclosetsim.Append(pathFromWC, wantToToilet) waterclosetsim.Start(540) <-waterclosetsim.Done waterclosetsim.PrintReport() 

仿真结果如下。
管道名称“水厕模拟”
模拟时间540
对象名称“办公室”
产生10

对象名称“来自WC的路径”
平均前进5.77

对象名称“ WC的路径”
平均前进5.22

对象名称“ WC的队列”
内容上限4
条目总数36
零词条8
允许零项22.22%
当前内容4
平均含量1.78
平均时间/跨24.11
平均时间/跨度(无零条目)31.00

对象名称“ WC”
平均前进14.69
平均利用率87.04
号码输入32.00
设施中的事务2

对象名称“想上厕所”
平均前进95.85

最多有4人在排队,一个人立即上厕所8次,在工作日内使用率是87.04%。 在我看来,最重要的是人们排队等候大约半小时(31分钟)。 可能是由于只有一个厕所,实际上是因为人们平均在其中坐了14.69分钟。

添加了另一个厕所后,我看到排队人数减少到了3人,立即有29人进入了厕所。 但最重要的是,预期下降了近三倍。

结论


在膝盖上创建的框架非常简单,但仍然受到限制。 计划将其功能增加到GPSS级别。 该框架的实用价值在于能够快速轻松地在Go上组装仿真模型并获得结果。

该代码发布在GitHub上

Source: https://habr.com/ru/post/zh-CN448076/


All Articles