Seeding Random Numbers in PHP

9th March 2021 - 7 minutes read time

Computers are not that great at creating random numbers as the methods they use are deterministic. That is, they start from a number (called a seed) and apply maths to that number to generate pseudorandom numbers. Most random numbers generated by computers programs use a seed provided by the system, which is generally the current time. If you can guess the initial value then you can start to work out what the random sequence of numbers generated is. Most random numbers, are therefore not suitable for encryption.

There has been a lot of work done to allow computers to generate random numbers. Famously, the CloudFlare offices in San Fransisco have a wall of lava lamps that are used to generate randomness in order to create strong cryptography for their services. The site www.random.org uses atmospheric data to generate random numbers, and provides a service that can be used to incorporate randomness into your application.

If you deliberately set a seed of a random number generator then the numbers generated will be the same every time. So why would you ever want to deliberately seed a random number generator? There are a couple of uses that this might have.

  • Many games use a seeded random number to create their levels. By using a seed to generate the levels the same levels can be generated for different players. This also makes debugging the games easier as if the same situation can be generated every time then the developer can re-create the situation and look at the variables involved. The same is true for any program that uses random functions to generate data. If you seed that randomness then you can predictably control the output of the program, which makes testing a lot easier.
  • In the early days of computers seeded random numbers were used as a means of compression. So instead of saving all the data to disk, a seeded random number generator was used to recreate all of the data from a single value and an algorithm. A great example of this is the game Elite where all of the data for the hundreds of star systems you could visit was stored as a single seeded random number generator.

Creating Random Numbers

The rand() function is used in PHP to generate a random number between two numbers using the Mersenne Twister random number generator algorithm. The following example will generate a number between 1 and 100.

$randomNumber = rand(1, 100);

Every time we call this function we get a different random number. In PHP there is also a mt_rand() function, but as of PHP 7.1.0 both the rand() and mt_rand() use the same underlying algorithm to generate their numbers.

Creating Seeded Random Numbers

To seed a random number you just need to call srand() at the start of the PHP script, before you call rand().

srand(12345);
$randomNumber = rand(1, 100);

Now, with the seed added, every time we call the random number we get a deterministic value. That is, calling rand() with a set seed will produce the same result. The script above on its own will produce the value '91' every single time it is run.

If we run this rand() function in a loop then we can generate a list of random numbers that will always be the same.

srand(12345);
$randomNumbers = [];

for ($i = 0; $i < 5; ++$i) {
  $randomNumbers[] = rand(1, 100);
}

print_r($randomNumbers);

This will always print out the following.

Array
(
    [0] => 91
    [1] => 82
    [2] => 86
    [3] => 54
    [4] => 85
)

Generating A Seeded Random Image

An example of how this sequence is always the same is by generating an image filled with seeded random data. The following block of code will generate a small image with random pixels in it, using a seed to generate the position of the pixels within the image. 

$seed = 1;
srand($seed);

$gd = imagecreatetruecolor(250, 250);
$white = imagecolorallocate($gd, 255, 255, 255);

for ($i = 0; $i < 1000; $i++) {
  $x = rand(1, 250);
  $y = rand(1, 250);

  imagesetpixel($gd, round($x),round($y), $white);
}

imagepng($gd, 'file' . $seed . '-' . time() . '.png');

This produces the following image.

Seeded random number image.

Running this for a second time produces exactly the same image again. This second image is a different image (no really, it is) and was created a few seconds after the first one.

Seeded random number image.

An Experiment With Seeds

As a bit of an experiment I wanted to see which random seen was the 'best' at creating random numbers. Yes, I realise the futility of this statement, but I thought it was worth looking into. I created some code that looked at 10,000,000 numbers as seeds to a random number generator and showed which one had the highest occurrence of non-repeated numbers.

// Initialise 'best' variables.
$bestSeed = 0;
$bestCount = 0;

for ($i = 0; $i < 10000000; $i++) {
  // Set the seed.
  srand($i);

  // (re)set the numbers array.
  $numbers = [];

  while (TRUE) {
    // Generate a random number.
    $number = rand(1, 100);
    // If the number is in the array of numbers already created, set match to true to stop loop.
    if (in_array($number, $numbers)) {
      break(1);
    }
    // Add the number to the numbers array.
    $numbers[] = $number;
  }

  // Count the numbers we added to the array.
  $count = count($numbers);

  if ($bestCount < $count) {
    // If the count is higher than the current best count then reset the count.
    $bestCount = $count;
    $bestSeed = $i;
  }
}

echo 'Seed ' . $bestSeed . ' = ' . $bestCount . ' numbers';

After a few seconds of running this came up with the following result.

Seed 1777423 = 53 numbers

This means that it took 1,777,423 different seeds before we found a random number sequence that contained 53 different numbers. We didn't find any higher scoring seeds for the next 8.2 million numbers.

As I said before, using rand() or mt_rand() to generate randomness for encryption purposes is dangerous as the values can be guessed. Using these functions with a seed means that it's actually a lot easier to figure out what random numbers were generated if the seed used was discovered.

Add new comment

The content of this field is kept private and will not be shown publicly.