C/C++ Arena

Step 7 of 7

Random numbers and shuffling

Games, simulations and tests all need random numbers. C's standard library has a simple generator in <stdlib.h>:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    srand((unsigned) time(NULL));        // seed once, from the clock
    int counts[7] = {0};
    for (int i = 0; i < 6000; i++) {
        int roll = rand() % 6 + 1;       // 1 to 6
        counts[roll]++;
    }
    int even = 1;
    for (int face = 1; face <= 6; face++) {
        if (counts[face] < 800 || counts[face] > 1200) even = 0;
    }
    printf("every face came up about 1000 times: %s\n", even ? "yes" : "no");
    return 0;
}
every face came up about 1000 times: yes

Pseudo-random

rand() isn't truly random. It's a formula that turns each number into the next, so the whole sequence is decided by the seed. That has consequences:

Shuffling fairly

To shuffle an array, the correct algorithm is the Fisher-Yates shuffle. Walk from the last element down to the second; for each position i, pick a random position j from 0 to i (inclusive) and swap the two:

for i from n - 1 down to 1:
    j = random number from 0 to i
    swap a[i] and a[j]

Every ordering comes out exactly equally likely. The tempting shortcut, swapping each element with a random position anywhere in the array, is biased: for 3 elements it makes 3 × 3 × 3 = 27 equally likely choices, which can't be split evenly among the 6 possible orders, so some orders come up more often than others.

Your turn: write roll(sides), returning a random number from 1 to sides, and shuffle(a, n), a Fisher-Yates shuffle of the n elements of a. The tests seed the generator themselves and check that every order of a 3-element array is equally likely, so the biased shortcut won't pass.

Previous: 2D arrays