Solution for Programmming Exercise 4.4
This page contains a sample solution to one of the exercises from Introduction to Programming Using Java.
Exercise 4.4:
This exercise builds on Exercise 4.3. Every time you roll the dice repeatedly, trying to get a given total, the number of rolls it takes can be different. The question naturally arises, what's the average number of rolls to get a given total? Write a function that performs the experiment of rolling to get a given total 10000 times. The desired total is a parameter to the subroutine. The average number of rolls is the return value. Each individual experiment should be done by calling the function you wrote for Exercise 4.3. Now, write a main program that will call your function once for each of the possible totals (2, 3, ..., 12). It should make a table of the results, something like:
Total On Dice Average Number of Rolls ------------- ----------------------- 2 35.8382 3 18.0607 . . . .
The solution uses the subroutine, rollFor, from Exercise 4.3. That subroutine will throw an exception if its parameter is not valid. However, in my program, I know that the values that I pass to the rollFor subroutine are valid and that no exception will occur. So, there is no need to use a try..catch statement to handle the exception
The main() program simply prints a heading for the output, then uses a for loop to compute and print the data for each of the possible rolls from 2 to 12. It is not difficult to write it, with a little care to get the formatting right (using formatted output).
The only thing left is to write a function to find the average number of rolls to get a given total on the dice. The average will be a real number, so the return type of the function is double. The subroutine has a parameter of type int that specifies the number we are rolling for. I'll call the parameter "roll". An algorithm for the subroutine is
Let totalRolls = 0 Repeat 10000 times: Call rollFor(roll) to run the experiment once Add the returned value to totalRolls Compute the average by dividing totalRolls by 10000 Return the average
In my program, I use a named constant, NUMBER_OF_EXPERIMENTS, to specify the number of experiments to be performed. This constant replaces the value 10000, making it easier to read the program and easier to change the number of experiments if I decide I want to do more experiments or fewer. This gives the subroutine:
static double getAverageRollCount( int roll ) { int rollCountThisExperiment; // Number of rolls in one experiment. int rollTotal; // Total number of rolls in all the experiments. double averageRollCount; // Average number of rolls per experiment. rollTotal = 0; for ( int i = 0; i < NUMBER_OF_EXPERIMENTS; i++ ) { rollCountThisExperiment = rollFor( roll ); rollTotal += rollCountThisExperiment; } averageRollCount = ((double)rollTotal) / NUMBER_OF_EXPERIMENTS; return averageRollCount; }
It is important that roll be in the range of possible totals on a pair of dice. If it is not, the program will enter an infinite loop in the rollFor() subroutine. In this case, I have chosen not to throw an exception if the parameter is invalid, since I know that the actual parameter is valid in this program.
Note that when the average is computed, a type-cast is used to convert rollTotal to type double. This is necessary since rollCount and NUMBER_OF_EXPERIMENTS are integers, and the computer would evaluate the quotient rollCount / NUMBER_OF_EXPERIMENTS as an integer.
By the way, this subroutine could be substantially abbreviated at the expense of being somewhat less easy to understand:
static double getAverageRollCount( int roll ) { int rollTotal = 0; // Total number of rolls in all the experiments. for ( int i = 0; i < NUMBER_OF_EXPERIMENTS; i++ ) rollTotal += rollFor( roll ); return ((double)rollTotal) / NUMBER_OF_EXPERIMENTS; }
/** * This program preforms the following type of experiment: Given a desired * total roll, such as 7, roll a pair of dice until the 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. The average is * computed and printed out for each possible roll = 2, 3, ..., 12. */ public class DiceRollStats { /** * The number of times that the the experiment "roll for a given total" * is to be repeated. The program performs this many experiments, and * prints the average of the result, for each possible roll value, */ static final int NUMBER_OF_EXPERIMENTS = 10000; public static void main(String[] args) { double average; // The average number of rolls to get a given total. System.out.println("Total On Dice Average Number of Rolls"); System.out.println("------------- -----------------------"); for ( int dice = 2; dice <= 12; dice++ ) { average = getAverageRollCount( dice ); System.out.printf("%10d%22.4f\n", dice, average); // Use 10 spaces to output dice, and use 22 spaces to output // average, with 4 digits after the decimal. } } /** * Find the average number of times a pair of dice must be rolled to get * a given total. The experiment of rolling for the given total is * repeated NUMBER_OF_EXPERIMENTS times and the average number of rolls * over all the experiments is computed. * Precondition: The given total must be be between 2 * and 12, inclusive. * @param roll the total that we want to get on the dice * @return the average number of rolls that it taks to get the specified * total */ static double getAverageRollCount( int roll ) { int rollCountThisExperiment; // Number of rolls in one experiment. int rollTotal; // Total number of rolls in all the experiments. double averageRollCount; // Average number of rolls per experiment. rollTotal = 0; for ( int i = 0; i < NUMBER_OF_EXPERIMENTS; i++ ) { rollCountThisExperiment = rollFor( roll ); rollTotal += rollCountThisExperiment; } averageRollCount = ((double)rollTotal) / NUMBER_OF_EXPERIMENTS; return averageRollCount; } /** * Simulates rolling a pair of dice until a given total comes up. * Precondition: The desired total is between 2 and 12, inclusive. * @param N the total that we want to get on the dice * @return the number of times the dice are rolled before the * desired total occurs * @throws IllegalArgumentException if the parameter, N, is not a number * that could possibly come up on a pair of dice */ static int rollFor( int N ) { if ( N < 2 || N > 12 ) throw new IllegalArgumentException("Impossible total for a pair of dice."); int die1, die2; // Numbers between 1 and 6 representing the dice. int roll; // Total showing on dice. int rollCt; // Number of rolls made. rollCt = 0; do { die1 = (int)(Math.random()*6) + 1; die2 = (int)(Math.random()*6) + 1; roll = die1 + die2; rollCt++; } while ( roll != N ); return rollCt; } } // end DiceRollStats