Please make sure you adhere to the policies on academic honesty.
Please see the general programming webpage for details about the programming environment for this course, and specifically for directions in how to submit your programming assignment electronically.
The files you need for this assignment can be downloaded here.
This setup can actually be used in modeling a variety of different applications, and it has been given the name "Bin Packing" in the literature. Unfortunately, minimizing the number of bins appears to be a very difficult problem to solve efficiently (there are too many possibilities!). Fortunately a number of heuristics have been studied, and some of these seem to get quite good results.
A second heuristic is known as "worst-fit-decreasing." This heuristics considers items one-at-a-time, again placing each considered item into the bin with the most remaining space (if such a bin will hold the item). The difference between this heuristic and the last is that items are considered in order of decreasing weight, rather than in the original order. To do this, all items are initially placed into a priority queue and at each step the item with largest remaining weight is removed from this queue and then placed into a bin. (note that for this to work, you will have to adjust your view of priorities so that the "minKey" is the one with the largest weight). Looking at our previous example, the items would get considered in the order .81, .68, .29 and .11, and thus packed into two bins. By no means are these the only two approaches to bin packing. If anyone is interested in the history of the problem, we will be happy to provide references. But for this assignment, we will focus on these two heuristics.
The driver accepts the following five command line arguments, as specified by the user. The first four of these are required; the fifth is optional.
At this point, the driver calls a routine worstFit which is written by you to simulate the worst-fit heuristic. This routine returns the overall number of bins used. Secondly, the driver calls a routine worstFitDecreasing written by you to simulate the worst-fit-decreasing heuristic.
Finally, although generating random data is useful for experiments, it is troublesome when developing and testing your program. The difficulty is that when a bug arises, you generally want to fix the bug and then re-test on the same data. But if data is generated at random, you will likely get a different data set when you re-test. For this reason, we will give you a way to set a "seed" which is used by the random number generator. In this way, you can re-test on a previous data set by providing the identical seed.
We are providing you with the PriorityQueue interface based on the description in section 7.1.3 of the textbook. Specifically, you will notice that this interface is based on the abstraction where keys are Object's and where a Comparator is used to compare keys. You must give two different priority queue implementations. Both are based on creating an underlying array for representing the priority queue. To simplify matters, the priority queue constructor will accept a parameter maxsize which gives a predefined limit on the number of items which might be held (thus allowing you to initialize your array).
The first implementation, titled SlowPQ, should be based on keeping the items in an unordered array. This is quite similar to the discussion in Section 7.2.1, but even easier as we will use an array directly rather than a Sequence.
The second implementation, titled FastPQ, will be based on a heap as specified in Section 7.3.2. Although we intuitively think of a heap as a binary tree, we will ask you to design your implementation based on the array representation of a heap (equivalently the "vector" representation as discussed on page 305 of the text). Again, you will use an array of Item's, but rather than keep them unordered, you will partially order them based on the heap property. Section 7.3.3 of the text gives an implementation of a priority queue with a heap, based on using the BinaryTree interface. Though this is different syntactically than what you must do, you may choose to look closely in understanding the process.
As we mentioned, your primary task is to correctly implement both of these heuristics. To get you going, we have provided you a template file Binpack.java which gives the two method definitions. As you start to think about the task you should realize that the concept of a priority queue will serve you in several ways. For instance, we already mentioned that the worst-fit-decreasing heuristic needs the use of a priority queue so that weights can be considered in decreasing order. But this is not the only use. Both heuristics should also make good use of a priority queue to keep track of all currently opened bins, always identifying the particular bin with the most remaining capacity. As you might see, these two uses of a priority queue are quite different. They hold different types of elements and they are based on a possibly different criteria for priorities. Fortunately, the abstraction provided by the PriorityQueue interface allows us this flexibility.
Each of the heuristics should be written as a routine taking three parameters:
PriorityQueue myPQ; if (useFastPQ) myPQ = new FastPQ(--parameters--); else myPQ = new SlowPQ(--parameters--); /* now you can start using it */ if (myPQ.isEmpty()) ...
Finally, the functionality required by the verbose flag must be
a forethought in planning your overall program. The issue is the
following. The routine is written to return a single int value
stating the overall number of bins which were used in the solution.
However in our original application, it does a person no good to
simply say that the solution uses 59 bins - a program must give
some sort of mapping to explain which items should be grouped in each
such bin. So for this reason, we want your program to be able to
produce verbose output which gives such a mapping. Going back to the
earlier sample, possible verbose output for the worst-fit heuristic
might read (though you are free to format the information as you wish):
Bin 1: weight=0.40 [items: 0.29 0.11]
Bin 2: weight=0.81 [items: 0.81]
Bin 3: weight=0.68 [items: 0.68]
At the same time, we will want to perform some large experiments. Verbose output would be very distracting if packing 1000000 items and producing such output would skew the running times. Even more important, on large experiments memory will become scarce. The ideal student will realize that the structure used for representing a bin can be greatly simplified if one is not required to produce the verbose output. Streamlined code will allow significantly larger experiments to be performed.
We ask that you perform some experiments which will be reported in your 'readme' file. This will allow us to compare the quality of the solutions produced by the two heuristics as well as the efficiency of the two priority queue implementations.
You must submit a plain-text readme file with the following information:
You have a choice in how to gather this information. One way is to sit at the computer for a while and gather piece after piece of information using our driver. A wise student might feel this is a mindless use of time and surely can be automated by a new or modified driver. For your benefit, we will tell you that our driver relies internally on the method BinpackDriver.runTrial() with parameters (int N, double u, boolean verbose, boolean fastPQ, long seed), the seed being optional. You could write a loop to call this method repeatedly with various parameter values.
This program might seem intimidating because of the many required tasks. Here is our suggestion for getting going:
Writing this part of the code will give you a way already to make sure that your priority queue implementation works correctly. Furthermore, writing this code will already require you to get some practice in how to make use of a priority queue, perhaps how to use a Double as a key, and in creating a new class which implements a Comparator.
Please submit all source code which you use other than those files which were directly provided by us for this assignment.
The assignment is worth 10 points. In general, we will try to apportion the points based on the following break down (though we will use some of our own judgment in the end):
You will notice, however, that the driver prints out the list of original weights formatted so that they are rounded to the nearest three decimal places. If you are interested in a lesson on formatting numbers in Java, you may wish to change your output to match this convention. To do so is straightforward, but a bit cumbersome. Here is what the driver does:
// create an object which is able to format numbers for you java.text.NumberFormat f = java.text.NuberFormat.getInstance(); f.setMaximumFractionDigits(3); f.setMinimumFractionDigits(3); // start printing out as many formatted numbers as you wish System.out.print(f.format(sample_double)); System.out.print(f.format(another_double));