I guess here comes my first question on SO.
I'm currently working on a website and I have to generate 6 numbers between 1 and 29 (one of each max) for a lottery. As they can be in any order, I simply sort them afterwards.
If I'm not mistaken, it should mean that there's (29*28*27*26*25*24) / 6! = 475020 different possible combinations.
I've tried different ways of genereting the sequences, either using mt_rand or random_int (from random_compat) but when I test it with something like 10k iterations, I always get around 100 duplicates, even though they are like 465k combinations still available.
Here are the code samples I've been trying :
// Using an array and mt_rand (or random_int, giving same results)
// Also tried shuffling the array instead of simply reindexing it, not better
$values = range(1, 29);
while(count($values) > 6) {
    unset($values[mt_rand(0, count($values) - 1)]);
    $values = array_values($values);
}
// Creating the array from random numbers (same results using random_int)
$values = array();
while (count($values) < 6) {
    $r = mt_rand(1, 29);
    if (in_array($r, $values)) {
        continue;
    } else {
        $values[] = $r;
    }
}
So well... My questions are :
Thanks !
Lyn.
PS : Looked through many questions, but didn't find anything filling my needs, sorry if I didn't look well enough !
Just to make a few things clear: Using random_int (which makes use of /dev/urandom or openssl_random_pseudo_bytes) doesn't improve anything, which I thought would. And I don't want to use any external API (like random.org) if possible.
Using random_int (which makes use of /dev/urandom or openssl_random_pseudo_bytes) doesn't improve anything, which I thought would.
Sure it does, it's just not something you can identify visually. mt_rand() and rand() only allow about 232 possible seeds and 232 possible outputs and, most importantly, have deterministic sequences: if you know a few outputs, you can predict the rest until it's reseeded.
Your operating system's CSPRNG doesn't have any such limits. Knowing a handful of random_int() outputs (which in PHP are limited to 232 possible values on 32-bit systems, 264 on 64-bit systems) will not give you any information on future outputs.
I'm currently working on a website and I have to generate 6 numbers between 1 and 29 (one of each max) for a lottery. As they can be in any order, I simply sort them afterwards.
Okay, that's a neat idea. You definitely want a CSPRNG here.
when I test it with something like 10k iterations, I always get around 100 duplicates, even though they are like 465k combinations still available.
As others have pointed out, this is the birthday problem/paradox at play.
If you need a solution, try something like this:
function random_unique_select($num, array $possible_values)
{
    $sizeof = count($possible_values);
    if ($num > $sizeof) {
        throw new InvalidArgumentException('$num is too large');
    }
    $selected = [];
    for ($i = 0; $i < $num; ++$i) {
        // Grab a random int [0, ... N - 1]
        $r = random_int(0, $sizeof - 1);
        // Copy the selected value into $selected
        $selected[] = $possible_values[$r];
        // Delete it from the range of possible values
        unset($possible_values[$r]);
        // N has grown smaller by 1
        --$sizeof;
        // Reset keys; we want this to be zero-indexed.
        $possible_values = array_values($possible_values);
    }
    return $selected;
}
$lottery = random_unique_select(6, range(1,29));
Demos:
openssl_random_pseudo_bytes())If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With