32-ATM模拟
12.7.3 ATM模拟
现在已经拥有模拟ATM所需的工具。程序允许用户输入3个数:队列的最大长度、程序模拟的持续时间(单位为小时)以及平均每小时的客户数。程序将使用循环——每次循环代表1分钟。在每分钟的循环中,程序将完成下面的工作。
1.判断是否来了新的客户。如果来了,并且此时队列未满,则将它添加到队列中,否则拒绝客户入队。
2.如果没有客户在进行交易,则选取队列的第一个客户。确定该客户的已等候时间,并将wait_time计数器设置为新客户所需的处理时间。
3.如果客户正在处理中,则将wait_time计数器减1。
4.记录各种数据,如获得服务的客户数目、被拒绝的客户数目、排队等候的累积时间以及累积的队列长度等。
当模拟循环结束时,程序将报告各种统计结果。
一个有趣的问题是,程序如何确定是否有新的客户到来。假设平均每小时有10名客户到达,则相当于每6分钟有一名客户。程序将计算这个值,并将它保存在min_per_cust变量中。然而,刚好每6分钟来一名客户不太现实,我们真正(至少在大部分时间内)希望的是一个更随机的过程——但平均每6分钟来一名客户。程序将使用下面的函数来确定是否在循环期间有客户到来:
bool newcustomer(double x)
{
return (std::rand() * x / RAND_MAX < 1);
}
其工作原理如下:值RAND_MAX是在cstdlib文件(以前是stdlib.h)中定义的,是rand()函数可能返回的最大值(0是最小值)。假设客户到达的平均间隔时间x为6,则rand()* x /RAND_MAX的值将位于0到6之间。具体地说,平均每隔6次,这个值会有1次小于1。然而,这个函数可能会导致客户到达的时间间隔有时为1分钟,有时为20分钟。这种方法虽然很笨拙,但可使实际情况不同于有规则地每6分钟到来一个客户。如果客户到达的平均时间间隔少于1分钟,则上述方法将无效,但模拟并不是针对这种情况设计的。如果确实需要处理这种情况,最好提高时间分辨率,比如每次循环代表10秒钟。
程序清单12.12给出了模拟的细节。长时间运行该模拟程序,可以知道长期的平均值;短时间运行该模拟程序,将只能知道短期的变化。
程序清单12.12 bank.cpp
// bank.cpp -- using the Queue interface
// compile with queue.cpp
#include <iostream>
#include <cstdlib> // for rand() and srand()
#include <ctime> // for time()
#include "queue.h"
const int MIN_PER_HR = 60;
bool newcustomer(double x); // is there a new customer?
int main()
{
using std::cin;
using std::cout;
using std::endl;
using std::ios_base;
// setting things up
std::srand(std::time(0)); // random initializing of rand()
cout << "Case Study: Bank of Heather Automatic Teller\n";
cout << "Enter maximum size of queue: ";
int qs;
cin >> qs;
Queue line(qs); // line queue holds up to qs people
cout << "Enter the number of simulation hours: ";
int hours; // hours of simulation
cin >> hours;
// simulation will run 1 cycle per minute
long cyclelimit = MIN_PER_HR * hours; // # of cycles
cout << "Enter the average number of customers per hour: ";
double perhour; // average # of arrival per hour
cin >> perhour;
double min_per_cust; // average time between arrivals
min_per_cust = MIN_PER_HR / perhour;
Item temp; // new customer data
long turnaways = 0; // turned away by full queue
long customers = 0; // joined the queue
long served = 0; // served during the simulation
long sum_line = 0; // cumulative line length
int wait_time = 0; // time until autoteller is free
long line_wait = 0; // cumulative time in line
// running the simulation
for (int cycle = 0; cycle < cyclelimit; cycle++)
{
if (newcustomer(min_per_cust)) // have newcomer
{
if (line.isfull())
turnaways++;
else
{
customers++;
temp.set(cycle); // cycle = time of arrival
line.enqueue(temp); // add newcomer to line
}
}
if (wait_time <= 0 && !line.isempty())
{
line.dequeue (temp); // attend next customer
wait_time = temp.ptime(); // for wait_time minutes
line_wait += cycle - temp.when();
served++;
}
if (wait_time > 0)
wait_time--;
sum_line += line.queuecount();
}
// reporting results
if (customers > 0)
{
cout << "customers accepted: " << customers << endl;
cout << " customers served: " << served << endl;
cout << " turnaways: " << turnaways << endl;
cout << "average queue size: ";
cout.precision(2);
cout.setf(ios_base::fixed, ios_base::floatfield);
cout << (double) sum_line / cyclelimit << endl;
cout << " average wait time: "
<< (double) line_wait / served << " minutes\n";
}
else
cout << "No customers!\n";
cout << "Done!\n";
return 0;
}
// x = average time, in minutes, between customers
// return value is true if customer shows up this minute
bool newcustomer(double x)
{
return (std::rand() * x / RAND_MAX < 1);
}
注意: 编译器如果没有实现bool,可以用int代替bool,用0代替false,用1代替true;还可能必须使用stdlib.h和time.h代替较新的cstdlib和ctime;另外可能必须自己来定义RAND_MAX。
下面是程序清单12.10~程序清单12.12组成的程序长时间运行的几个例子:
Case Study: Bank of Heather Automatic Teller
Enter maximum size of queue: 10
Enter the number of simulation hours: 100
Enter the average number of customers per hour: 15
customers accepted: 1485
customers served: 1485
turnaways: 0
average queue size: 0.15
average wait time: 0.63 minutes
Done!
Case Study: Bank of Heather Automatic Teller
Enter maximum size of queue: 10
Enter the number of simulation hours: 100
Enter the average number of customers per hour: 30
customers accepted: 2896
customers served: 2888
turnaways: 101
average queue size: 4.64
average wait time: 9.63 minutes
Done!
Case Study: Bank of Heather Automatic Teller
Enter maximum size of queue: 20
Enter the number of simulation hours: 100
Enter the average number of customers per hour: 30
customers accepted: 2943
customers served: 2943
turnaways: 93
average queue size: 13.06
average wait time: 26.63 minutes
Done!
注意,每小时到达的客户从15名增加到30名时,等候时间并不是加倍,而是增加了15倍。如果允许队列更长,情况将更糟。然而,模拟没有考虑到这个事实——许多客户由于不愿意排很长的队而离开了。
下面是该程序的另外几个运行示例。从中可知,即使平均每小时到达的客户数不变,也会出现短期变化。
Case Study: Bank of Heather Automatic Teller
Enter maximum size of queue: 10
Enter the number of simulation hours: 4
Enter the average number of customers per hour: 30
customers accepted: 114
customers served: 110
turnaways: 0
average queue size: 2.15
average wait time: 4.52 minutes
Done!
Case Study: Bank of Heather Automatic Teller
Enter maximum size of queue: 10
Enter the number of simulation hours: 4
Enter the average number of customers per hour: 30
customers accepted: 121
customers served: 116
turnaways: 5
average queue size: 5.28
average wait time: 10.72 minutes
Done!
Case Study: Bank of Heather Automatic Teller
Enter maximum size of queue: 10
Enter the number of simulation hours: 4
Enter the average number of customers per hour: 30
customers accepted: 112
customers served: 109
turnaways: 0
average queue size: 2.41
average wait time: 5.16 minutes
Done!