Getting an integer value from a given range with an even distribution:
This function I created to solve the problem of modulo results causing overlap of ranged results (which gave an uneven distribution).
What I mean for those not as familiar with the problem:
Using bytes for base 256 (base 16) and attempting to find a value in a range of values that may be for example 10-20 (a spread of 11) will not divide evenly, so values (using mod) will overlap and give more priority to some numbers than others.
Instead of calculating based on byte values, I used the byte values as keys to sort. This is very fast, and does not require large multiplications of data space that easily go over the value of Max Int.
Additionally: To make the user-supplied arguments not care about order I am using a handy swap function I found in the wild in conjunction with my function below.
// swap function
function swap(&$a,&$b) { list($a,$b)=array($b,$a); } // swap 2 variables-- no temp variable needed!
// function to get a random value within a given range of integers
function get_secure_random_ranged_value($max=99, $min=0) // handles 1 or 2 arguments, order does not matter
{
$sortarray = array();
$lo = (int)$min;
$hi = (int)$max;
if ($lo > $hi) swap($lo,$hi);
$data_range = abs($hi - $lo) + 1; // +1 includes both the lowest 'zero' value and highest value of range
$bytes_per_key = 4; // Max: ffff hex = 4,294,967,296 dec (over 4 billion) -- large span of random values covers massive datasets
$num_bytes = $data_range * $bytes_per_key;
$byte_string = (bin2hex(openssl_random_pseudo_bytes($num_bytes))); // only one call needed to get string of bytes
$byte_blocksize = $bytes_per_key << 1; // shift multiply by 2 since a byte is 2 characters wide
while ($key = substr($byte_string,0,$byte_blocksize)) { // get next byte block from string
$byte_string = substr($byte_string,$byte_blocksize); // remove selected byte block from string
$sortarray[]=$key; // populate the array with keys temporarily as array values
}
$sortarray = array_flip($sortarray); // swap to use the byte values as keys
ksort($sortarray); // randomize by keys
return array_shift($sortarray) + $lo; // grab top value from array and add it to the lowest value in the range
}
//
// example getting values from 0 to 21:
//
for ($i=1;$i<=10;$i++) { $rnd = get_secure_random_ranged_value(21); echo "-> result: ".($rnd)." <br />\n"; }
//
// example getting values from 14 to 21:
//
for ($i=1;$i<=10;$i++) { $rnd = get_secure_random_ranged_value(14,21); echo "-> result: ".($rnd)." <br />\n"; }
//
// sample results from 14-21
//
-> result: 14
-> result: 18
-> result: 20
-> result: 15
-> result: 20
-> result: 16
-> result: 21
-> result: 15
-> result: 16
-> result: 17