CS302 -- Lab 5

Banking Simulation


Lab Objective

This lab is designed to give you experience using priority queues and to give you experience writing simulations.


Setting Up

You may use either the Fields library or C++ iostreams for this lab. I would recommend using the Fields.h and the Fields.cpp files from practice homework 4.

You should use the C++ STL priority_queue class. www.cppreference.com is a pretty self-explanatory reference for the priority queue class and my stl notes show you how to declare the priority queue class.


Simulation

In the remainder of this lab you will implement the banking simulation described in class. The class notes serve as an important resource for this lab so whenever you have questions about design or implementation, the class notes are a good place to look. The goal of running the simulation will be to determine the minimal number of tellers required in order to ensure that no more than x% of customers have to wait more than y minutes in line. Once you have completed the simulation program, you will be able to experimentally increase or decrease the number of tellers in order to determine the minimal number of tellers.

The program you will write will be named bankSimulator and will take the following arguments:

bankSimulator time_limit num_tellers wait_threshold
	   mean_transaction_time dist_file seed
The arguments have the following meaning:

  1. time_limit: How many time units the simulation should run. You can also think of the time_limit as the number of seconds the bank is open for business.
  2. num_tellers: The number of tellers to be used in the simulation.
  3. wait_threshold: The maximum time, in seconds, that a customer should have to wait.
  4. mean_transaction_time: The mean time, in seconds, for a teller to complete a transaction with a customer.
  5. dist_file: The histogram file used to general arrival times.
  6. seed: An integer seed for the random number generator.

An example invocation of the simulation program would look as follows:

cetus3> bankSimulator 72000 3 100 120 expon_120 53

Number of customers = 577
Average customer waiting time = 4
Maximum customer wait = 125
Percentage of customers who waited longer than 100 seconds for a teller:  0.7

Teller   Idle Time    Idle 
     0       49459    68.7
     1       47918    66.6
     2       48850    67.8

The binary for simulation can be found in /home/bvz/courses/302/labs/lab6/bankSimulator. If you have any questions about how your program should execute, the way the format of the output should look, or what the correct values for the output are, you should execute the simulation binary. The bankSimulator in my directory has an optional additional argument, called doprint which goes at the end of the argument list. By default it is 'no' and you do not have to include it. If you say 'yes', then my simulator will print out certain information about the events as they are processed. This information might help you in creating and debugging your simulation program.


Output

Each execution of your simulation program should produce five outputs, formatted as shown above:

  1. Number of customers: This count is the total number of customers processed during the simulation.

  2. Average customer waiting time: This is the average time, in seconds, that a customer had to spend waiting for a teller (i.e., waiting in line).
  3. Maximum customer wait: This number is the maximum time a customer spent waiting for a teller.

  4. Percentage of customers who waited longer than 'wait_threshold' seconds for a teller: This is the percentage of customers that had to wait longer than the wait_threshold for a teller. Note that 'wait_threshold' should be replaced by the actual wait_threshold.

  5. The idle time for each teller in seconds and in percent. The percent idle time is the idle time for each teller divided by the length of the simulation.

Although some of these statistics are not required to answer the minimal teller question, they would be useful to a bank executive trying to make decisions about the number of tellers to hire. For example, a bank executive probably would also want to factor into his or her decision the average time that a customer spends waiting for a teller and the maximum time a customer has to wait for a teller.


Details

You should use the implementation scheme described in class for the bank teller problem (also see pages 224-225 of Weiss). In other words, you should have a priority queue that is ordered by timestamped events. If your simulation has k tellers, then the priority queue will have up to k departure events and some number of arrival events. You should also use the classes shown in class and you should use inheritance to handle the events.

Specifically you will need to write the following routines:

  1. A routine to read in the arguments and check their validity.
  2. A routine to initialize the bank simulation (e.g., creating the tellers, creating the arrival events, and initializing the statistics).
  3. A routine for executing the event loop.
  4. A routine for printing the statistics.
  5. Routines for handling the processing of arrival and departure events.
  6. Routines for generating random numbers from a histogram file and for a uniform distribution.
  7. Anything else that comes up as you write your program.

Some of the routines might be lumped together into the main procedure. For example routines 2-4 might go into the main procedure.


What Has Already Been Done For You

In order to help get you started, some code has already been prepared for you. This code can be found in the following files:


Other Helpful Information

Here is some other information you need to know about include files and linking as well as a couple hints:

  1. random and srandom are declared in stdlib.h. These are the random number generators you should use.

  2. If you declare a global variable as a template class, do not try to allocate it statically. You will get completely unintelligable linker messages if you do. For example:
         list<Event *> a;
         
    will get the unintelligable linker messages if a is a global variable. However, the following declaration and initialization will work just fine:
         list<Event *> *a = new list<Event *>();
         

  3. The only dependencies your makefile has to concern itself with are the dependencies among the .cpp files that you create and the .h files you modify. The .cpp files that we have provided are assumed to be never changing and therefore you do not have to worry about writing makefile dependencies for them.

  4. For this lab you are being given the latitude to create your own names for your .cpp files. You must however use multiple files because the code that defines your event methods should not be placed in the same file as your simulation code.

  5. Process all customers that arrive before or at the time limit.

  6. When a teller becomes free add the teller to the end of the free teller queue.

  7. Your results may vary slightly from mine because you may do tasks that require random numbers in a different order. Item 3 below is an example of where tasks might be performed in a different order. Several things that you should be aware of:

    1. Don't forget that if tellers are in the free_teller queue at the end of the simulation that you have to count their idle time from the time they were placed in the queue until the end of the simulation.

    2. Don't forget to count as teller idle time the time between the beginning of the simulation and the first customer they process.

    3. I generate the service time for a customer at the same time that I generate the customer's arrival time. If you wait to generate a service time until you assign a customer to a teller then your results will differ somewhat from mine.
  8. You can use a map's upper_bound method to return the first entry in the map that is greater than the key for which you are searching. This method can come in handy when you generate a random number and then need to find the corresponding value for your histogram distribution. It is ok if the random number matches a key in your map and upper_bound returns the next highest key. My executable does return the next highest key. To ensure that all values are returned with the right frequency I generate random numbers between 0 and (total-1).

What to Submit

You need to submit two different things for this lab:

  1. The .h files we created for you, your .cpp files, your makefile, the expon_120 histogram file, and the answers file described below. If you fail to complete the lab then also submit a README file that describes what you did complete; if you do not do so you will not receive partial credit. It is also a good idea to have a doprint option that prints out information on arrival/departure events as they occur and the assignment of people to tellers or the waiting queue. Again doing so will allow the TAs to give you partial credit if your program does not always produce the correct output.

  2. The minimal number of tellers required to assure that fewer than x% of customers have to wait more than y seconds for a teller where x, y, the mean transaction time and the seed have the following values:
         x = 5, y = 180, mean_transaction_time = 120, seed = 53
         x = 5, y = 180, mean_transaction_time = 240, seed = 137
         x = 2, y = 180, mean_transaction_time = 300, seed = 333
         x = 2, y = 300, mean_transaction_time = 240, seed = 217
         
    All simulations should be run for 144000 seconds and your answers should be placed in a file called answers.