## Solution for

Programming Exercise 5.3

THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

Exercise 5.3:This problem uses thePairOfDiceclass from Exercise 5.1 and theStatCalcclass from Exercise 5.2.The program in Exercise 4.4 performs the experiment of counting how many times a pair of dice are rolled before a given total comes up. It repeats this experiment 10000 and then reports the average number of rolls. It does this whole process for each possible total (2, 3, ..., 12).

Redo that exercise. But instead of just reporting the average number of rolls, you should also report the standard deviation and the maximum number of rolls. Use a

PairOfDiceobject to represent the dice. Use aStatCalcobject to compute the statistics. (You'll need a newStatCalcobject for each possible total, 2, 3, ..., 12. You can use a new pair of dice if you want, but it's not necessary.)

Discussion

The program from Exercise 4.4 defines a function,

rollFor(N), that performs the basic experiment once. It rolls the dice until the total on the dice isN, and it returns the number of rolls. Using aPairOfDiceobject,dice, the body of this subroutine becomesint rollCt = 0; // Number of rolls made. do { dice.roll(); rollCt++; } while ( dice.getTotal() != N ); return rollCt;This is significantly simpler than the original version. But where does the dice object come from? One possibility is to create a new

PairOfDiceobject at the beginning of the function. This will work, but then a new object is created each time the function is called. In the program we are writing, the function is called 110,000 times. It seems a waste to manufacture 110,000 pairs of dice when one would do. To avoid this, I create the dice as a static member variable:private static PairOfDice dice = new PairOfDice();The variable must be

staticsince it is used in thestaticfunction,rollFor. Sincediceis a static member variable, it is created and initialized when the class is first loaded and it exists as long as the program is running. TherollFor()method always uses this one pair of dice. (Some people might prefer to create the dice as a local variable in themain()routine. The dice could be passed as a parameter to therollFor()method. Then,rollFor(N,dice)would mean "roll for a total ofNusing this pair of dice.")The original program also had a method called

getAverageRollCount()to find the average number of rolls, when the basic experiment is repeated 10000 times. We could rename this togetRollCountStatsand use it to compute all the statistics, not just the average. The actual computation is to be done by aStatCalcobject. Letstatsbe a variable that refers to that object. The results of each experiment will be fed into this object, something like this:for ( int i = 0; i < 10000; i++ ) { // Assume "total" is the number we are rolling for. rollCountThisExperiment = rollFor( total ); stats.enter( rollCountThisExperiment ); }At the end of this process,

statsis ready to report the statistics. All you have to do is call its functions, such asstats.getMean().In my program, I use the named constant

NUMBER_OF_EXPERIMENTSinstead of the literal number, 10000. I abbreviate the for loop tofor ( int i = 0; i < NUMBER_OF_EXPERIMENTS; i++ ) stats.enter( rollFor(total) );and, since it has become so short, I deleted the subroutine and moved the

forloop into themain()routine. Themain()routine prints the output in neat columns, using the version ofTextIO.put()that has a column-width specification. For example, "TextIO.put(total,6);" prints the value oftotalin a column of width 6. (The hardest part was figuring out how wide to make the columns!). After printing out some headings for the columns, themain()routine saysfor ( int total = 2; total <= 12; total++ ) { StatCalc stats; // An object that will compute the statistics. stats = new StatCalc(); for ( int i = 0; i < NUMBER_OF_EXPERIMENTS; i++ ) { // Do the experiment of counting the number of rolls // required to roll the desired total, and enter the // number of rolls into stats' dataset. stats.enter( rollFor(total) ); } TextIO.put(total, 6); TextIO.put(stats.getMean(), 18); TextIO.put(stats.getStandardDeviation(), 19); TextIO.put(stats.getMax(), 14); TextIO.putln(); }The body of the

forloop processes one of the possible totals on a pair of dice, and it produces one line of output with the statistics for that total. A newStatCalcobject is created for each execution of theforloop. This is necessary because we want separate statistics for each total. A singleStatCalcobject would just accumulate all the data into a single dataset.

By the way, you might wonder what would happen if I had not eliminated the

getRollCountStats()subroutine? In that case, the statistics data is generated in the subroutine and is used in themain()routine. So, the sameStatCalcobject has to be used in both routines. There are several ways to handle this. The variable,stats, could be a static member variable. Then it could simply be used in both routines. Alternatively, theStatCalcobject could be passed as a parameter to the subroutine. Or, as a final alternative, the object could be created in the subroutine and sent back to themain()routine as a return value. Let's look at this last possibility. The subroutine would be:static StatCalc getRollCountStats( int total ) { StatCalc calc; // An object to compute the statistics. calc = new StatCalc(); for ( int i = 0; i < NUMBER_OF_EXPERIMENTS; i++ ) calc.enter( rollFor(total) ); return calc; // Send back the object, with the statistics. }In the main program, this would be used as follows:

for ( int total = 2; total <= 12; total++ ) { StatCalc stats; // The stats for this total stats = getRollCountStats( total ); // Get stats from subroutine. TextIO.put(total, 6); TextIO.put(stats.getMean(), 18); TextIO.put(stats.getStandardDeviation(), 19); TextIO.put(stats.getMax(), 14); TextIO.putln(); }Note in particular that not every object variable needs to be initialized with a new object. In this case, an object is computed elsewhere. The variable,

stats, is set to refer to that existing object.

The Solution

public class DiceRollStats2 { /* This program preforms the following type of experiment: Given a desired total roll, such as 7, roll a pair of dice until th given total comes up, and count how many rolls are necessary. Now do the over and over, and find the average number of rolls. The number of times the experiment is repeated is given by the constant, NUMBER_OF_EXPERIMENTS. Several statistics are computed and printed out for each possible roll = 2, 3, ..., 12: the average number of rolls, the standard deviation, and the maximum number of rolls. */ static final int NUMBER_OF_EXPERIMENTS = 10000; private static PairOfDice dice = new PairOfDice(); // A single pair of dice, which will be used for all // the experiments. public static void main(String[] args) { TextIO.putln("Dice Total Avg # of Rolls Stand. Deviation Max # of Rolls"); TextIO.putln("---------- -------------- ---------------- --------------"); for ( int total = 2; total <= 12; total++ ) { StatCalc stats; // An object that will compute the statistics. stats = new StatCalc(); for ( int i = 0; i < NUMBER_OF_EXPERIMENTS; i++ ) { // Do the experiment of counting the number of rolls // required to roll the desired total, and enter the // number of rolls into stats' dataset. stats.enter( rollFor(total) ); } TextIO.put(total, 6); TextIO.put(stats.getMean(), 18); TextIO.put(stats.getStandardDeviation(), 19); TextIO.put(stats.getMax(), 14); TextIO.putln(); } } // end main static int rollFor( int N ) { // Roll the dice repeatedly until the total on the // two dice comes up to be N. N MUST be one of the numbers // 2, 3, ..., 12. (If not, this routine will go into an // infinite loop!). The number of rolls is returned. int rollCt = 0; // Number of rolls made. do { dice.roll(); rollCt++; } while ( dice.getTotal() != N ); return rollCt; } } // end class DiceRollStats2

[ Exercises | Chapter Index | Main Index ]