Last active
November 14, 2020 09:21
-
-
Save vip3r011/c8001f129d6d93b7c303888457ea04e3 to your computer and use it in GitHub Desktop.
distribution
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function randomInt(int $min, int $max) :int | |
{ | |
if (!is_int($min)) { | |
throw new \Exception('First parameter ($min) must be an integer'); | |
} | |
if (!is_int($max)) { | |
throw new \Exception('Second parameter ($max) must be an integer'); | |
} | |
if ($min > $max) { | |
throw new \Exception('First parameter ($min) must be no greater than second parameter ($max)'); | |
} | |
if ($min === $max) { | |
return $min; | |
} | |
// $range is a PHP float if the expression exceeds PHP_INT_MAX. | |
$range = $max - $min + 1; | |
if (is_float($range)) { | |
$mask = null; | |
} else { | |
// Make a bit mask of (the next highest power of 2 >= $range) minus one. | |
$mask = 1; | |
$shift = $range; | |
while ($shift > 1) { | |
$shift >>= 1; | |
$mask = ($mask << 1) | 1; | |
} | |
} | |
$tries = 0; | |
do { | |
$bytes = random_bytes(PHP_INT_SIZE); | |
// Convert byte string to a signed int by shifting each byte in. | |
$value = 0; | |
for ($pos = 0; $pos < PHP_INT_SIZE; $pos += 1) { | |
$value = ($value << 8) | ord($bytes[$pos]); | |
} | |
if ($mask === null) { | |
// Use all bits in $bytes and check $value against $min and $max instead of $range. | |
if ($value >= $min && $value <= $max) { | |
return $value; | |
} | |
} else { | |
// Use only enough bits from $bytes to cover the $range. | |
$value &= $mask; | |
if ($value < $range) { | |
return $value + $min; | |
} | |
} | |
$tries += 1; | |
} while ($tries < 123); | |
// Worst case: this is as likely as 123 heads in as many coin tosses. | |
throw new \Exception('Unable to generate random int after 123 tries'); | |
} | |
/*source: https://stackoverflow.com/questions/40644040/how-to-generate-a-random-number-from-a-uniform-distribution-in-php | |
just simulating dice rolls here | |
*/ | |
function testRand($randFunction, $groupsNumber = 6, $rollsNumber = 4000000) | |
{ | |
$frequencies = array_fill(0, $groupsNumber, 0); | |
foreach (range(1, $rollsNumber) as $ignored) { | |
$frequencies[$randFunction(0, $groupsNumber - 1)]++; | |
} | |
echo PHP_EOL, "------- results for `$randFunction` -------", PHP_EOL; | |
$cum = 0; | |
foreach ($frequencies as $index => $frequency) { | |
$percent = $frequency * 100 / $rollsNumber; | |
$cum += $percent; | |
echo sprintf("%d\t|\t%4d\t%6.2f\t%6.2f", | |
$index, $frequency, $percent, $cum), | |
PHP_EOL; | |
} | |
} | |
testRand('randomInt'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment