Java Arena
Step 5 of 5

Simulations, and checking random results

This last step puts random numbers to work in whole programs: simulations that answer questions about chance, checks that a random result is right, and a word on where Random doesn't belong.

Simulations

Some questions about chance are hard to work out by hand but easy to try: play the game many times, and count. That's a simulation. How many rolls of a die does it take, on average, to get the first six?

import java.util.Random;

public class Main {
    public static void main(String[] args) {
        Random random = new Random(1);
        int games = 10000;
        int totalRolls = 0;
        for (int i = 0; i < games; i++) {
            totalRolls += rollsUntilSix(random);
        }
        System.out.println("Average rolls: " + (double) totalRolls / games);
    }

    public static int rollsUntilSix(Random random) {
        int rolls = 0;
        int roll = 0;
        while (roll != 6) {
            roll = random.nextInt(6) + 1;
            rolls++;
        }
        return rolls;
    }
}
Output
Average rolls: 5.9766

Close to 6, which is also what probability math says: with a chance of 1 in 6 on each roll, it takes 6 rolls on average. In a single game it can take 1 roll, or 20. Simulations earn their keep when the math gets hard: a whole board game with several players, special squares and cards is much easier to simulate than to calculate.

  • The more games a simulation plays, the closer its answer usually is to the true one. Try 100 games instead of 10000, with a few different seeds, and the averages are much further apart.
  • The seed makes the whole simulation repeatable. Anyone who runs it gets exactly the same average, and can check your work.

Checking random results

A test of a program with random numbers has two tools. With a seed, it can compare the exact output, as this module's checks do. Without one, it can still check what must be true of every run, however the numbers fall. Such facts are called properties:

  • every value is in its range: a die roll is from 1 to 6
  • every possible value comes up, given enough tries: in 6000 rolls, all six faces
  • a draw without repeats has no repeats, and a shuffled list has the same elements as before
  • a share is close to its chance: of 100000 tries with a 30% chance, between 28% and 32% hit

The last one needs a margin, since random results wobble. With enough tries, a wide margin is practically never crossed by chance: when such a check fails, look for the mistake in the code. The hidden tests of this module's challenges check properties like these.

Here's a property of a shuffle. However the list is shuffled, sorting it gives back the sorted original:

ArrayList<String> guests = new ArrayList<>();
guests.add("Noor");
guests.add("Emil");
guests.add("Sade");
guests.add("Luca");
ArrayList<String> sorted = new ArrayList<>(guests);
Collections.sort(sorted);
Random random = new Random();
boolean same = true;
for (int i = 0; i < 1000; i++) {
    ArrayList<String> shuffled = new ArrayList<>(guests);
    Collections.shuffle(shuffled, random);
    Collections.sort(shuffled);
    if (!shuffled.equals(sorted)) {
        same = false;
    }
}
System.out.println("The same guests after 1000 shuffles: " + same);
Output
The same guests after 1000 shuffles: true

Not for secrets

Random is fine for games, simulations and tests, but not for passwords, secret codes or anything else that someone would like to guess. Its formula is public, and someone who sees enough of its numbers can work out the ones that come next. Java has a separate class for secrets, SecureRandom in the package java.security, which is built so that its numbers can't be worked out that way.

Your task

Challenge 1 of 3

A coin flip streak counter. Write the method longestStreak(Random random, int flips), which flips a coin flips times and returns the longest streak: the most flips in a row that landed on the same side. Flip each coin with one random.nextBoolean() call (true is heads, false is tails), so that a seed gives the same flips as the check's. flips is at least 1. For example, the flips heads, tails, tails, tails, heads have a longest streak of 3. The check runs:

Random random = new Random(42);
System.out.println(longestStreak(random, 10));
System.out.println(longestStreak(random, 100));

and expects:

2
6

Challenge 2

Collections.shuffle only changes the order of a list. What does this program print?

Challenge 3

To finish the module, a random seating plan for a dinner party. In SeatingPlan.java, write the class SeatingPlan with:

  • a constructor SeatingPlan(ArrayList<String> guests, int tables, Random random). It shuffles a copy of the guest list with Collections.shuffle(copy, random) (the list it's given keeps its order), and then deals the shuffled guests out to the tables in turn, the way cards are dealt: the first guest to table 1, the second to table 2, and so on, back to table 1 after the last table.
  • table(int number), which returns an ArrayList<String> of the guests at that table, in the order they were dealt. Tables are numbered from 1.
  • toString(), which returns one line for each table, like Table 1: Noor, Luca, with the names separated by a comma and a space, and a line break between the tables.

The check runs:

ArrayList<String> guests = new ArrayList<>();
guests.add("Noor");
guests.add("Emil");
guests.add("Sade");
guests.add("Luca");
guests.add("Mei");
guests.add("Arjun");
guests.add("Rosa");
SeatingPlan plan = new SeatingPlan(guests, 3, new Random(4));
System.out.println(plan);
System.out.println(plan.table(2).size() + " at table 2");

and expects:

Table 1: Emil, Arjun, Luca
Table 2: Sade, Rosa
Table 3: Noor, Mei
2 at table 2