CS302 -- Lab 6
Priority Queues
- CS302 -- Fundamental Algorithms
- Fall, 1999
- Brad Vander Zanden
- Due Dates
- Due at the beginning of your second lab period (the week of
October 11):
Your simulation.h file showing
your proposed design for the lab (email the file to Hui if
you cannot make the beginning of lab).
- Due Wednesday, October 27 at 2:30PM for the Wednesday lab and
Friday, October 29 at 11:15AM for the Friday lab:
The binary executable for
the lab, your source files, and your experimental results.
Lab Objective
This lab is designed to give you experience using priority queues
and to give you experience writing simulations, which was the first
application for object-oriented programming.
Setting Up
You should copy the following files from the
/ruby/homes/ftp/pub/bvz/classes/cs302/labs/lab6 directory to your
directory:
- BinaryHeap.h
- Dlist.h
- dsexceptions.h
- vector.h
- BinaryHeap.cc
- Dlist.cc
- vector.cc
Problem Statement
In this lab you will implement a simple simulation of a factory using
priority queues. The simulation will assume that you have a set of
machines that can process an item into a product. The items arrive
randomly and are put in a waiting queue until they can be assigned to
a free machine. The items incur an inventory cost while they are waiting.
The goal of the simulation is to determine the optimal number of machines
that will minimize the total cost of both the machines and the
inventory cost. Once you have completed the simulation program, you will
be able to experimentally increase or decrease the number of machines in
order to determine the optimal number of machines.
The program you will write will be named simulation and will take the
following arguments:
simulation time_limit num_machines machine_cost_per_unit_time
inventory_cost_per_unit_time
The arguments have the following meaning:
- time_limit: How many time units the simulation should run.
- num_machines: The number of machines to be used in the simulation.
- machine_cost_per_unit_time: The cost of each machine per unit time.
This argument should be an integer. You should assume that the
machine costs the same amount per unit time, regardless of whether
or not it is processing an item. For example, the cost per unit
time might represent depreciation.
- inventory_cost_per_time_unit: The cost incurred by keeping an
item waiting for a machine for one time unit. If an item waits
10 time units for a machine and the cost per time unit is 2, then
the total inventory cost for this item will be 20. This argument
should be an integer.
An example invocation of the simulation program would look as follows:
% simulation 1200 3 2 1
Number of items processed = 233
Maximum wait = 521
Number of items waiting to be processed = 215
Inventory cost: 93442
Machine cost: 7200
Total cost: 100642
The binary for simulation can be found in
/ruby/homes/ftp/pub/bvz/classes/cs302/bin/simulation. 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.
Output
Each execution of your simulation program should produce six outputs,
formatted as shown above:
- Number of items finished: This count is the total number of
items that were completely finished before the time limit was
exceeded. This count will be equal to the number of departure
events processed. When the time limit is exceeded, there may be
one or more departure events still waiting to be processed because
their time stamps exceed the time limit. Do not count
the items associated with these departure events as finished.
- Maximum wait: This number is the maximum time a completely
finished item spent waiting in the wait queue.
- Number of items waiting to be processed: This number is the number
of items still in the wait queue when the time limit was exceeded.
- Inventory cost: This number is the number obtained by adding the
inventory cost of each item. An item's inventory cost is its time
spent waiting in the wait queue multiplied by the inventory cost
per unit time. In computing this cost, you need to include the
inventory cost of items left in the wait queue but not processed.
For example, if an item arrives 20 time units before the time limit
is exceeded and is never processed, then its 20 time units of
waiting should be included in the inventory cost.
- Machine cost: This number is the cost of the machines and is obtained
by multiplying the number of machines by the cost per machine.
- Total cost: This number is the total of the inventory and machine
costs. It is the number you are looking to minimize.
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 machines, then the priority queue will
have up to k departure events and one arrival event (i.e., a
maximum of k+1 events).
Arrival Events
An arrival event should perform the
following actions:
- Record the arrival time in the item's record.
- Determine whether there is a free machine that can process this
item. If there is a free machine, then the item should be assigned
to the free machine and the start time should be noted in the
item's record (it will be identical to the arrival time). If there
is no free machine, then the item should be placed at the end of
a waiting queue. The waiting queue can be implemented as an ordinary
queue.
- Generate the next arrival event and add it to the priority queue.
Departure Events
A departure event should perform the following actions:
- Compute statistics for the item. These statistics are described
later on.
- Determine if there are any items waiting to be processed and if so,
assign the first item from the waiting queue to the now free machine.
When the item is assigned to the machine, its start time should
be noted (i.e., the current time should be noted). Otherwise the
machine should be marked as free.
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:
- BinaryHeap.h: A header file for
binary heaps. This file contains the declarations used in the
Weiss book. It has been modified so that you pass pointers to
methods rather than references. These modifications have been
made because coming from the C world, you are probably more
comfortable with passing pointers than references. You should
examine this header file to see how it differs from the book's
declarations. The BinaryHeap class is a template class that
requires you to specify the type of object you plan to store
in the heap. The class makes two assumptions:
- The objects you store in the heap have defined the < operator so
that they can be compared, and
- In the heap you store pointers to objects rather than the
objects themselves. For example, if you were storing
RosterRecords in the heap, you would declare it as:
BinaryHeap<RosterRecord *> queue;
rather than:
BinaryHeap<RosterRecord> queue;
- BinaryHeap.cc: The implementation file
for binary heaps. You should not have to look at this file unless
you are interested in seeing how binary heaps/priority queues
are implemented.
- Dlist.cc and Dlist.h: You used this template class before in lab 4.
- vector.h, dsexceptions.h, vector.cc: These files are used by
either the Dlist or BinaryHeap classes. You do not have to know
how they operate nor do you have to include them in any of your files.
In other words, copy them to your directory but do not worry about them.
- We have defined the random number generation techniques that you
will use to generate arrival and departure times:
- Arrival Times: An arrival time should be computed using
the following equation:
period = 3.14159265359 / 300
time_to_next_arrival
= rand() % (10 + (int)(8 * (sin(current_time * period))))
arrival_time = current_time + time_to_next_arrival
The sin function causes arrival times to oscillate periodically.
A complete period is 600 time units (2pi = (600 * 3.14159 / 300)).
The upper limit on the next arrival time can vary from 2-18 time
units, depending on the current time. The random number generator
rand randomly chooses a time between 0 and the current
upper limit. rand is a system provided function that
generates random numbers.
- Departure Times: A departure time should be computed using
the following equation:
departure_time = current_time + (8 + (rand() % 12))
In other words, the time to process an item is uniformly
distributed between 8 and 19 time units.
What You Need To Do
Unlike previous labs you are not being given the data structure
declarations for this lab. In the first week of the lab you will
be expected to come up with your own design and hand it in at the
beginning of the second lab period for your lab.
Once you have decided on a design, you will need to program the simulation
and conduct your experiments.
Design Tips
Here are some design tips:
- You should probably have four objects, corresponding to items, machines,
arrival events, and departure events respectively. Arrival and
departure events share some common attributes and methods so they
should share a superclass called Event.
- You should use the event loop shown in class to drive your simulation.
Recall that the event loop assumed that each event had a time field
that could be used to record the current time and an action method
that could be invoked so that the event performs the appropriate
action.
- You should consider having a machine implement a Start method.
The start method would 1) mark the machine as busy, 2) assign a departure
time to the item, 3) store a pointer to the machine in the item so that
when the item is finished it will know which machine is free, and
4) note the start time in the item's record. Note that the Start method
should be called whenever an item is assigned to a machine, either
because it just arrived and there is a free machine or because it
is being removed from the waiting queue.
Other Helpful Information
Here is some other information you need to know about include files and
linking:
- rand is declared in stdlib.h.
- sin is declared in math.h.
- When you use the math library (i.e., any function in math.h), you
need to explicitly tell the loader to use the math library. The way
you do this is to place -lm at the end of your link line. For
example:
g++ -o temp temp.o -lm
It is critical that -lm go at the end of the link line, otherwise
you may get strange link errors or core dumps (not in all versions of
g++ but some). The -l flag tells the linker to link a library and the
m tells it to link the math library.
- When you compile your files, you will need to add a
-fhandle-exceptions flag. This flag causes the compiler
to insert code that handles C++ exceptions, which are used by
some of the provided files.
- 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:
Dlist<Event *> a;
will get the unintelligable linker messages if a is a global
variable. However, the following declaration and initialization will
work just fine:
Dlist<Event *> a = new Dlist<Event *>();
- The only dependencies your makefile has to concern itself with
are the dependencies among the .h and .cc files that you create.
The .h and .cc 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.
- For this lab you are being given the latitude to create your
own names for your .h and .cc files.
What to Submit
You need to submit three different things for this lab:
- Your proposed simulation.h file for this lab (due at the beginning
of the second lab period for this lab).
If you cannot make the beginning of lab, send the .h file to
Hui via email before that time.
- Your simulation files. Use the standard electronic submit procedure to
send your makefile and
source files to Hui. The name of the executable binary file created by
your makefile should be simulation.
- The number of machines that minimize total cost with the following
three parameter sets:
time_limit = 1200 1200 1200
machine_cost_per_unit_time = 4 2 2
inventory_cost_per_time_unit = 1 1 2
If you are a manager, you can use simulations such as these to try
out what-if scenarios. For example, cases 1 and 2 might allow a
manager to find out how many more machines should be purchased if
the purchase price on a machine can be halved. Cases 1 and 3 would
allow a manager to find out how two different interest rates might
impact the purchasing decision (higher interest rates mean higher
inventory costs).