Math.random() on the AP CS A Exam

The AP CS A Exam features problems that require:

Solutions must be exactly correct to earn the point. Off by 1 will not earn the point.

Generate random numbers in a given range

Math.random() technique

The statement
double r = Math.random();

results in r having a random value in the range
0.0 <= r < 1.0

There are 3 things you can do with the result returned by Math.random().

  1. Multiply
  2. Cast to an int
  3. Add (or subtract)

Steps that do not apply can be skipped. Steps should not be reordered. The issue with reordering the steps isn’t that it doesn’t work, but that it makes it harder to check that the resulting range is exactly correct.

For example, the technique requires:
double r = (int) (Math.random() * 10) + 1;

instead of:
double r = (int) (Math.random() * 10 + 1);

even though both generate values in the same range.

Write what you have, which always starts as the original range above. Write out what you want. Every time you update the Math.random() expression, update what you have.

When you’re done, you are either right and you know you’re right or you’re wrong and you know you’re wrong. If you’re wrong, start over. If you need to start over, look at your previous attempt to determine what needs to be changed. Do not simply modify your previous attempt. Modifying your previous attempt makes it harder to check that the range is exactly what you want.

Example 1

Step 1

double r = Math.random();
have: 0.0 <= r < 1.0
want: 0.0 <= r < 5.0

Step 1 is writing out the unaltered call to Math.random(), the original range returned by Math.random(), and the numeric range we want.

When we want a double, the upper end of the range is typically specified with <.

Step 2

double r = Math.random() * 5;
have: 0.0 <= r < 5.0
want: 0.0 <= r < 5.0

Multiplying by 5 changes the high end of the range to 5.0 (and leaves the low end at 0.0 since 0.0 * 5 is 0.0).

We have what we want, so we’re done and we know we’re right.

Example 2

Step 1

double r = Math.random();
have: 0.0 <= r < 1.0
want: 7.0 <= r < 12.0

Step 2

double r = Math.random() * 5;
have: 0.0 <= r < 5.0
want: 7.0 <= r < 12.0

Multiplying by 5 changes the high end to 5.0.

Step 3

double r = Math.random() * 5 + 7;
have: 7.0 <= r < 12.0
want: 7.0 <= r < 12.0

Adding 7 changes both ends of the range.

We have what we want, so we’re done and we know we’re right.

Example 3

Step 1

int r = Math.random();
have: 0.0 <= r < 1.0
want: 0 <= r <= 5

When we want an int, the upper end of the range is typically specified with <=.

The original statement will not compile because Math.random() returns a double. This is fine because we don’t plan to keep the original statement.

Step 2

int r = Math.random() * 6;
have: 0.0 <= r < 6.0
want: 0 <= r <= 5

Multiplying by 6 changes the high end of the range to 6.0.

This statement will not compile either. This is still fine because we aren’t done.

Step 3

int r = (int) (Math.random() * 6);
have: 0 <= r <= 5
want: 0 <= r <= 5

When casting to an int, it is the quantity (Math.random() * 6) that we want to cast. Without the parentheses, the cast would apply to the return value from Math.random(), which would always yield 0.

Casting to an int changes the low end of the range to 0.

The high end of the range is more interesting. When we take a number strictly less than 6.0 and cast it to an int, the largest number we will ever get is 5. So the high end of the range becomes <= 5.

We have what we want, so we’re done and we know we’re right.

Example 4

Step 1

int r = Math.random();
have: 0.0 <= r < 1.0
want: 10 <= r <= 25

Step 2

int r = Math.random() * 16;
have: 0.0 <= r < 16.0
want: 10 <= r <= 25

Multiplying changes only the high end.

Step 3

int r = (int) (Math.random() * 16);
have: 0 <= r <= 15
want: 10 <= r <= 25

Casting to an int changes both ends. See Example 3 Step 3 for an explanation.

Step 4

int r = (int) (Math.random() * 16) + 10;
have: 10 <= r <= 25
want: 10 <= r <= 25

Adding changes both ends.

We have what we want, so we’re done and we know we’re right.

Example 4

We want a random valid index in str, a String that has been previously declared and initialized.

Step 1

int index = Math.random();
have: 0.0 <= index < 1.0
want: 0 <= index <= str.length() - 1

In this case, Step 1 requires converting the description into a numeric range. As with all situations in which we want an int, we use <= for the high end. (It is possible to express the high end as < str.length(). Doing so would make it slightly more difficult to check our result.)

Step 2

int index = Math.random() * str.length();
have: 0.0 <= index < str.length()
want: 0 <= index <= str.length() - 1

Step 3

int index = (int) (Math.random() * str.length());
have: 0 <= index <= str.length() - 1
want: 0 <= index <= str.length() - 1

We have what we want, so we’re done and we know we’re right.

The same approach works for generating a valid random index in an array or in an ArrayList.

It is very common for students to multiply by a variation of str.length() such as (str.length() - 1), not check the resulting range, and lose a point.

It is also common for students to add or subtract after casting, not check the resulting range, and lose a point.

Determine the range generated

Questions on the multiple choice section may be phrased such that they effectively ask you to generate numbers in a given range. Use the technique above for these.

Other questions start with an existing expression based on Math.random() and ask what range it generates. To answer these questions:

  1. Simplify or convert the expression such that it matches the format above. It might be necessary to break the expression into more than 1 expression, each of which simplifies to the format above.

  2. Write the range for an unmodified call to Math.random().

  3. Apply the multiplication to the range. Then apply the cast. Then apply the addition (or subtraction). Skip any that aren’t present.

Example

What is the range of values that could be stored in value as a result of the statement below?
double value = 5 + Math.random() * 12;

Step 1

The expression in the format above is
double value = Math.random() * 12 + 5;

The range generated by Math.random() is
have: 0.0 <= value < 1.0

Step 2

The range after multiplying by 12 is
have: 0.0 <= value < 12.0

Step 3

The range after adding 5 is
have: 5.0 <= value < 17.0

This is the final range.

Simulate a given probability

The statement
double r = Math.random();

results in r having a random value in the range
0.0 <= r < 1.0

The value return by Math.random() is evenly distributed within that range. (Any value within the range is equally likely.)

Using Math.random() to simulate a given probability does not require manipulating the output. Instead, we simply check if the result is less than the desired probability.

Example 1

Print "player wins" 35% of the time.

if(Math.random() < 0.35)
    System.out.println("player wins");

Example 2

Print "yes" 15% of the time, "no" 35% of the time, and "maybe" the other 50% of the time.

double r = Math.random();
if(r < 0.15)
    System.out.println("yes");
else if(r < 0.5)
    System.out.println("no");
else
    System.out.println("maybe");

We use only 1 random number, not 1 random number per possibility. We want the else if condition to be true 35% of the time. 0.15 + 0.35 is 0.5.

Exercises

Exercise 1

a and b are of type double and
a >= 0.0 && b > a

Give code to declare double r and initialize it to a random value in the range
a <= r < b

Exercise 2

c and d are of type int and
c >= 0 && d > c

Give code to declare int r and initialize it to a random value in the range
c <= r <= d

Exercise 3

ArrayList<String> names = new ArrayList<String>();
names.add("Brandon");
names.add("Brenda");
/* code to add many more names to names */

Give code to remove and print a randomly selected name from names. Each name must be equally likely to be selected.

Exercise 4

The code below simulates rolling an 8 sided die.
int roll = (int) (Math.random() * 8) + 1;

The range of values generated is
1 <= roll <= 8

Which of the following correctly simulate(s) the result of rolling two 8 sided dice and adding the values together?

Case I:   int sum = (int) (Math.random() * 8) + 1 + (int) (Math.random() * 8) + 1;

Case II:  int sum = 2 + (int) (Math.random() * 8) + (int) (Math.random() * 8);

Case III: int sum = ((int) (Math.random() * 8) + 1) * 2;

(A) I only
(B) II only
(C) III only
(D) I and II only
(E) I, II, and III

Solution & comments

See the Random number generation solutions or review them with AP CS Tutor Brandon Horn.

Comment on Generate random numbers with Math.random()

Additional Math.random() resources

Practice problems featuring Math.random()

AP Exam free response featuring Math.random()

The links below are to my solutions. Each solution has a link to the PDF with the free response questions for that year.