17.5 用队列进行模拟

好,队列能用了!现在我们用它来做一些更有趣的事。很多现实生活的情形都包含队列。例如,在银行或超级市场的顾客队列,在机场的飞机队列,多任务计算机系统上的任务队列等。可以用队列包来模拟这些情形。

例如,假设Sigmund Landers在商业街上设了一个提供建议的摊位。顾客可购买一分钟、两分钟或三分钟的建议。为确保交通通畅,商业街规章限制排成一队等待的顾客数目最多为10(很合适地用于程序的最大队列长度)。假设人们的出现是随机的,并且他们花在咨询上的时间随机地分布于三个选择(一分钟,两分钟或三分钟)上。那么Sigmund平均每小时要接待多少顾客?每位顾客平均要等多长时间?队平均有多长?队列模拟能回答这类问题。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

首先,让我们决定队列里面要放什么。您能够描述的是每一位顾客加入到队中的时间和他(她)需要咨询的时间。这提示我们要对Item类型做下面的定义:

阅读 ‧ 电子书库

为转化队列包来处理这个结构,您所要做的就是用这里对Item的typedef定义替换上个例子中使用的int类型。做完这些之后,您就不必再担心队列的具体工作机制,而是分析实际问题,即模拟Sigmund的等候队列。

有一种办法是这样的。让时间以1分钟为单位递增。在每分钟里,检查是否有一个新的顾客到来。如果有一个顾客到来并且队列未满,将此顾客添加到队列。这包括将此顾客的到达时间和他需要的咨询时间记录到一个Item结构中,而后将此项目添加到队列中。但如果队列已满,就拒绝此顾客加入。为了做统计,需要保存顾客的总数和被拒顾客(由于队列已满而不能进入队列的人)总数。

接下来处理队列首端。即,如果队列非空且Sigmund未被前面的顾客占用,则删除位于队列首端的项目。回忆一下,项目中存有此顾客加入队列的时间。通过比较这个时间和当前时间,就可得到此顾客在队列中的等待时间。这个项目还存有此顾客需要的咨询时间,这将决定此顾客占用Sigmund的时间。用一个变量跟踪这一等待时间。如果Sigmund正忙,没人可以“出列”。当然,此跟踪变量应能自动递减。

核心代码可以像下面这样,其中每一个循环对应于一分钟的活动:

阅读 ‧ 电子书库

阅读 ‧ 电子书库

以下是一些变量和函数的意义:

● min_per_cus是顾客到达的平均间隔时间。

● newcustomer()使用C的rand()函数确定在这一分钟里是否有顾客到达。

● turnaways是被拒顾客的数目。

● customers是加入到队列中的顾客数目。

● temp是描述新顾客的Item变量。

● customertime()设置temp结构的arrive和processtime成员。

● wait_time是Sigmund完成当前顾客咨询还需要的时间。

● line_wait是到目前为止队列中所有顾客已等待的总时间。

● served是实际服务的顾客数目。

● sum_line是到目前为止队列的累计总长。

想想看如果用散布在程序中的malloc()和free()函数与指向节点的指针来实现此程序的话,代码将会多么的混乱和晦涩。应用队列包使您集中精力于模拟的问题,而非编程细节。

程序清单17.9给出了模拟商业街建议摊队列的完整代码。按照第12章介绍的方法,使用了标准函数rand()、srand()和time()来产生随机数。要使用此程序,记住按照如下所示更新queue.h头文件中对Item的定义:

阅读 ‧ 电子书库

还要记住链接mall.c和queue.c的代码。

程序清单17.9 mall.c程序

阅读 ‧ 电子书库

阅读 ‧ 电子书库

阅读 ‧ 电子书库

程序允许用户指定模拟运行的小时数和每小时顾客的平均数。选择一个数目较大的小时数会得到一个较好的平均值,而选择一个较小的小时数会显示一定程度的随时间的随机变化。下面的运行结果说明了这些要点。注意到平均队长和等待时间在80小时和800小时的情况下基本相同,但这两个量在两个1小时的情况下相差很大,同时与长期平均值相差也很大。这是因为小数量的统计示例更易受相对变化的影响。

阅读 ‧ 电子书库

阅读 ‧ 电子书库

另一种使用程序的方法是保持小时数不变,而尝试用不同的每小时顾客平均数的情况。以下是两个探寻这种变化的示例:

阅读 ‧ 电子书库

注意到当顾客访问频率提高后平均等待时间急速上升。在每小时20个顾客(80小时模拟时间)的情况下,平均等待时间为1.35分钟。在每小时25个顾客的情况下,此数值攀升到3.50分钟;而在每小时30个顾客的情况下更是急剧增加到11.83分钟。并且,被拒顾客数从0上升到3进而上升到94。Sigmund可以根据这个分析结果来决定他是否需要另外一个摊位。