<?php
class RandomKeyMultiple {
private $max_range;
function __construct( $source ) {
// build the look-up array
foreach ( $source as $key => $value ) {
for ( $i = 0; $i < $value; $i++ ) {
$this->pool[] = $key;
}
}
$this->max_range = count($this->pool) - 1; }
function get_random_key() {
return $this->pool[$x];
}
}
class RandomKey {
private $steps = array(); private $last_key;
private $max_range;
function __construct( $source ) {
// sort in ascending order to partially avoid numerical issues
// calculate the partial sums. Considering OP's array:
//
// 1300 ----> 0
// 30000 ----> 1300
// 190000 ----> 31300
// 265000 ----> 221300 endind with $partial = 486300
//
$partial = 0;
$temp = 0;
foreach ( $source as $k => &$v ) {
$temp = $v;
$v = $partial;
$partial += $temp;
}
// scale the steps to cover the entire mt_rand() range
foreach ( $source as $k => &$v ) {
$v *= $factor;
}
// Having the most probably outcomes first, minimizes the look-up of
// the correct index
// remove last element (don't needed during checks) but save the key
$this->last_key = key($this->steps); }
function get_random_key() {
foreach ( $this->steps as $key => $value ) {
if ( $x > $value ) {
return $key;
}
}
return $this->last_key;
}
}
// *********** test section *************
function number_right( $num, $size ) {
}
function percentage_right( $num, $decimals, $size ) {
}
function test_randomness( $test, $original, $iterations ) {
$sum = 0;
foreach ( $original as $k => $v ) {
$buckets[$k] = 0;
$sum += $v;
}
for ( $i = 0; $i < $iterations; $i++ ) {
$buckets[$test->get_random_key()]++;
}
$duration = $stop - $start;
" samples (generated in ", number_format($duration,5), " seconds):\n", "======================================================\n",
" key occurences (%) (expected %) \n",
"------------------------------------------------------\n";
foreach ( $buckets as $i => $num ) {
number_right($num, 10),
percentage_right($num / $iterations, 3, 11),
percentage_right($original[$i] / $sum, 3, 10), "\n";
}
echo "------------------------------------------------------\n";
}
function test_steps( $arr, $name, $rep ) {
echo "\nTesting steps method with $name array.\n";
$test = new RandomKey($arr);
test_randomness($test, $arr, $rep);
}
function test_reps( $arr, $name, $rep ) {
echo "\nTesting repetitions method with $name array.\n";
$test = new RandomKeyMultiple($arr);
test_randomness($test, $arr, $rep);
}
$repetitions = 500000;
'head' => 1,
'tails' => 1
);
test_reps($coin, "coins", $repetitions);
$dice = array( // results of throwing two dices '2' => 1, // 1 + 1
'3' => 2, // 1 + 2 or 2 + 1
'4' => 3, // 1 + 3 or 2 + 2 or 3 + 1
'5' => 4, // 1 + 4 or 2 + 3 or 3 + 2 or 4 + 1
'6' => 5, // 1 + 5 or 2 + 4 or 3 + 3 or 4 + 2 or 5 + 1
'7' => 6, // ...
'8' => 5,
'9' => 4,
'10' => 3,
'11' => 2,
'12' => 1
);
test_reps($dice, "dice", $repetitions);
'0' => 265000, // Area
'1' => 190000,
'2' => 30000,
'3' => 1300
);
test_reps($arr2, "OP's", $repetitions);
'0' => 265000, // Area
'1' => 190000,
'2' => 30000,
'3' => 1300
);
test_steps($arr, "OP's", $repetitions);
"Washington" => 658.893, // thousands
"New York" => 8406,
"Chicago" => 2719,
"Pasadena" => 139.731,
"Seattle" => 652.405
);
test_steps($population, "population", $repetitions);
?>
PD9waHAKCiAgICBjbGFzcyBSYW5kb21LZXlNdWx0aXBsZSB7CiAgICAJcHJpdmF0ZSAkcG9vbCA9IGFycmF5KCk7CgkJcHJpdmF0ZSAkbWF4X3JhbmdlOwogCiAgICAJZnVuY3Rpb24gX19jb25zdHJ1Y3QoICRzb3VyY2UgKSB7CgkJCS8vIGJ1aWxkIHRoZSBsb29rLXVwIGFycmF5CiAgICAJCWZvcmVhY2ggKCAkc291cmNlIGFzICRrZXkgPT4gJHZhbHVlICkgewogICAgCQkJZm9yICggJGkgPSAwOyAkaSA8ICR2YWx1ZTsgJGkrKyApIHsKICAgIAkJCQkkdGhpcy0+cG9vbFtdID0gJGtleTsKICAgIAkJCX0KICAgIAkJfQogICAgCQkkdGhpcy0+bWF4X3JhbmdlID0gY291bnQoJHRoaXMtPnBvb2wpIC0gMTsKICAgIAl9CiAKICAgIAlmdW5jdGlvbiBnZXRfcmFuZG9tX2tleSgpIHsKICAgIAkJJHggPSBtdF9yYW5kKDAsICR0aGlzLT5tYXhfcmFuZ2UpOwogCiAgICAJCXJldHVybiAkdGhpcy0+cG9vbFskeF07CQkKICAgIAl9CiAgICB9CiAKICAgIGNsYXNzIFJhbmRvbUtleSB7CiAgICAJcHJpdmF0ZSAkc3RlcHMgPSBhcnJheSgpOwogICAgCXByaXZhdGUgJGxhc3Rfa2V5OwogICAgCXByaXZhdGUgJG1heF9yYW5nZTsKIAogICAgCWZ1bmN0aW9uIF9fY29uc3RydWN0KCAkc291cmNlICkgewogICAgCQkvLyBzb3J0IGluIGFzY2VuZGluZyBvcmRlciB0byBwYXJ0aWFsbHkgYXZvaWQgbnVtZXJpY2FsIGlzc3VlcwogICAgCQlhc29ydCgkc291cmNlKTsgIAogICAgCQkKICAgIAkJLy8gY2FsY3VsYXRlIHRoZSBwYXJ0aWFsIHN1bXMuIENvbnNpZGVyaW5nIE9QJ3MgYXJyYXk6CiAgICAJCS8vCiAgICAJCS8vICAgMTMwMCAtLS0tPiAgICAgICAwCiAgICAJCS8vICAzMDAwMCAtLS0tPiAgICAxMzAwCiAgICAJCS8vIDE5MDAwMCAtLS0tPiAgIDMxMzAwCiAgICAJCS8vIDI2NTAwMCAtLS0tPiAgMjIxMzAwICBlbmRpbmQgd2l0aCAkcGFydGlhbCA9IDQ4NjMwMAogICAgCQkvLwogICAgCQkkcGFydGlhbCA9IDA7CiAgICAJCSR0ZW1wID0gMDsKICAgIAkJZm9yZWFjaCAoICRzb3VyY2UgYXMgJGsgPT4gJiR2ICkgewogICAgICAgICAgICAgICAgJHRlbXAgPSAkdjsKICAgIAkJCSR2ID0gJHBhcnRpYWw7CiAgICAgICAgCQkkcGFydGlhbCArPSAkdGVtcDsKICAgIAkJfQoKICAgIAkJLy8gc2NhbGUgdGhlIHN0ZXBzIHRvIGNvdmVyIHRoZSBlbnRpcmUgbXRfcmFuZCgpIHJhbmdlCiAgICAJCSRmYWN0b3IgPSBtdF9nZXRyYW5kbWF4KCkgLyAkcGFydGlhbDsKICAgIAkJZm9yZWFjaCAoICRzb3VyY2UgYXMgJGsgPT4gJiR2ICkgewogICAgCQkgICAgJHYgKj0gJGZhY3RvcjsKICAgIAkJfQogICAgCQkKICAgIAkJLy8gSGF2aW5nIHRoZSBtb3N0IHByb2JhYmx5IG91dGNvbWVzIGZpcnN0LCBtaW5pbWl6ZXMgdGhlIGxvb2stdXAgb2YKICAgIAkJLy8gdGhlIGNvcnJlY3QgaW5kZXgKICAgIAkJJHRoaXMtPnN0ZXBzID0gYXJyYXlfcmV2ZXJzZSgkc291cmNlKTsKCiAgICAJCS8vIHJlbW92ZSBsYXN0IGVsZW1lbnQgKGRvbid0IG5lZWRlZCBkdXJpbmcgY2hlY2tzKSBidXQgc2F2ZSB0aGUga2V5CiAgICAJCWVuZCgkdGhpcy0+c3RlcHMpOwogICAgCQkkdGhpcy0+bGFzdF9rZXkgPSBrZXkoJHRoaXMtPnN0ZXBzKTsKICAgIAkJYXJyYXlfcG9wKCR0aGlzLT5zdGVwcyk7CiAgICAJfQogCiAgICAJZnVuY3Rpb24gZ2V0X3JhbmRvbV9rZXkoKSB7CiAgICAgCQkkeCA9IG10X3JhbmQoKTsKIAogICAgICAgICAgICBmb3JlYWNoICggJHRoaXMtPnN0ZXBzIGFzICRrZXkgPT4gJHZhbHVlICkgewogICAgCQkJaWYgKCAkeCA+ICR2YWx1ZSApIHsKICAgIAkJCQlyZXR1cm4gJGtleTsKICAgIAkJCX0KICAgIAkJfQogICAgCQlyZXR1cm4gJHRoaXMtPmxhc3Rfa2V5OwkJCiAgICAJfQogICAgCQogICAgfQogICAgCiAgICAvLyAqKioqKioqKioqKiAgdGVzdCBzZWN0aW9uICoqKioqKioqKioqKioKICAgIAogICAgZnVuY3Rpb24gbnVtYmVyX3JpZ2h0KCAkbnVtLCAkc2l6ZSApIHsKICAgIAlyZXR1cm4gc3RyX3BhZChudW1iZXJfZm9ybWF0KCRudW0pLCAkc2l6ZSwgJyAnLCBTVFJfUEFEX0xFRlQpOwogICAgfQoKICAgIGZ1bmN0aW9uIHBlcmNlbnRhZ2VfcmlnaHQoICRudW0sICRkZWNpbWFscywgJHNpemUgKSB7CiAgICAJcmV0dXJuIHN0cl9wYWQobnVtYmVyX2Zvcm1hdCgxMDAgKiAkbnVtLCAzKSwgJHNpemUsICcgJywgU1RSX1BBRF9MRUZUKTsKICAgIH0KCiAgICAKICAgIGZ1bmN0aW9uIHRlc3RfcmFuZG9tbmVzcyggJHRlc3QsICRvcmlnaW5hbCwgJGl0ZXJhdGlvbnMgKSB7CiAgICAgCiAgICAgICAgJHN1bSA9IDA7CiAgICAgICAgZm9yZWFjaCAoICRvcmlnaW5hbCBhcyAkayA9PiAkdiApIHsKICAgICAgICAgICAgJGJ1Y2tldHNbJGtdID0gMDsKICAgICAgICAgICAgJHN1bSArPSAkdjsKICAgICAgICB9CiAgICAJCiAgICAgICAgJHN0YXJ0ID0gbWljcm90aW1lKHRydWUpOwogICAgCWZvciAoICRpID0gMDsgJGkgPCAkaXRlcmF0aW9uczsgJGkrKyApIHsKICAgIAkJJGJ1Y2tldHNbJHRlc3QtPmdldF9yYW5kb21fa2V5KCldKys7CiAgICAJfQogICAgCSRzdG9wID0gbWljcm90aW1lKHRydWUpOwogICAgCSRkdXJhdGlvbiA9ICRzdG9wIC0gJHN0YXJ0OwogICAgCWFyc29ydCgkYnVja2V0cyk7CiAgICAJZWNobyAiRGlzdHJpYnV0aW9uIG9mICIsIG51bWJlcl9mb3JtYXQoJGl0ZXJhdGlvbnMpLAogICAgCQkgIiBzYW1wbGVzIChnZW5lcmF0ZWQgaW4gIiwgbnVtYmVyX2Zvcm1hdCgkZHVyYXRpb24sNSksICIgc2Vjb25kcyk6XG4iLAogICAgCQkgIj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuIiwKICAgIAkJICIgICAga2V5ICAgICAgICAgICAgICBvY2N1cmVuY2VzICAgICAoJSkgICAoZXhwZWN0ZWQgJSkgXG4iLAogICAgCQkgIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIjsKICAgIAlmb3JlYWNoICggJGJ1Y2tldHMgYXMgJGkgPT4gJG51bSApIHsKICAgIAkJZWNobyAiICAgICIgLCBzdHJfcGFkKCRpIC4gIjogIiwgMTYpLCAKICAgIAkJCSBudW1iZXJfcmlnaHQoJG51bSwgMTApLAogICAgCQkJIHBlcmNlbnRhZ2VfcmlnaHQoJG51bSAvICRpdGVyYXRpb25zLCAzLCAxMSksCiAgICAJCSAgICAgcGVyY2VudGFnZV9yaWdodCgkb3JpZ2luYWxbJGldIC8gJHN1bSwgMywgMTApLCAiXG4iOwogICAgCX0KICAgIAllY2hvICItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiI7CiAgICB9CiAgICAgCiAgICBmdW5jdGlvbiB0ZXN0X3N0ZXBzKCAkYXJyLCAkbmFtZSwgJHJlcCApIHsKICAgICAgICBlY2hvICJcblRlc3Rpbmcgc3RlcHMgbWV0aG9kIHdpdGggJG5hbWUgYXJyYXkuXG4iOwogICAgICAgICR0ZXN0ID0gbmV3IFJhbmRvbUtleSgkYXJyKTsKICAgICAgICB0ZXN0X3JhbmRvbW5lc3MoJHRlc3QsICRhcnIsICRyZXApOwogICAgfQogICAgCiAgICBmdW5jdGlvbiB0ZXN0X3JlcHMoICRhcnIsICRuYW1lLCAkcmVwICkgewogICAgICAgIGVjaG8gIlxuVGVzdGluZyByZXBldGl0aW9ucyBtZXRob2Qgd2l0aCAkbmFtZSBhcnJheS5cbiI7CiAgICAgICAgJHRlc3QgPSBuZXcgUmFuZG9tS2V5TXVsdGlwbGUoJGFycik7CiAgICAgICAgdGVzdF9yYW5kb21uZXNzKCR0ZXN0LCAkYXJyLCAkcmVwKTsKICAgIH0KICAgIAogICAgJHJlcGV0aXRpb25zID0gNTAwMDAwOwoKICAgICRjb2luID0gYXJyYXkoICAgICAKICAgIAkgJ2hlYWQnID0+IDEsICAgIAogICAgCSAndGFpbHMnID0+IDEgICAgCiAgICApOwogICAgdGVzdF9yZXBzKCRjb2luLCAiY29pbnMiLCAkcmVwZXRpdGlvbnMpOwoKICAgICRkaWNlID0gYXJyYXkoICAgICAvLyByZXN1bHRzIG9mIHRocm93aW5nIHR3byBkaWNlcwogICAgCSAnMicgPT4gMSwgICAgICAvLyAxICsgMQogICAgCSAnMycgPT4gMiwgICAgICAvLyAxICsgMiBvciAyICsgMQogICAgCSAnNCcgPT4gMywgICAgICAvLyAxICsgMyBvciAyICsgMiBvciAzICsgMSAKICAgIAkgJzUnID0+IDQsICAgICAgLy8gMSArIDQgb3IgMiArIDMgb3IgMyArIDIgb3IgNCArIDEKICAgIAkgJzYnID0+IDUsICAgICAgLy8gMSArIDUgb3IgMiArIDQgb3IgMyArIDMgb3IgNCArIDIgb3IgNSArIDEKICAgIAkgJzcnID0+IDYsICAgICAgLy8gLi4uCiAgICAJICc4JyA9PiA1LAogICAgCSAnOScgPT4gNCwKICAgIAknMTAnID0+IDMsCiAgICAJJzExJyA9PiAyLAogICAgCScxMicgPT4gMQogICAgKTsKICAgIHRlc3RfcmVwcygkZGljZSwgImRpY2UiLCAkcmVwZXRpdGlvbnMpOwoKICAgICRhcnIyID0gYXJyYXkoCiAgICAgICcwJyA9PiAyNjUwMDAsIC8vIEFyZWEKICAgICAgJzEnID0+IDE5MDAwMCwKICAgICAgJzInID0+IDMwMDAwLAogICAgICAnMycgPT4gMTMwMAogICAgKTsKICAgIHRlc3RfcmVwcygkYXJyMiwgIk9QJ3MiLCAkcmVwZXRpdGlvbnMpOwoKICAgICRhcnIgPSBhcnJheSgKICAgICAgJzAnID0+IDI2NTAwMCwgLy8gQXJlYQogICAgICAnMScgPT4gMTkwMDAwLAogICAgICAnMicgPT4gMzAwMDAsCiAgICAgICczJyA9PiAxMzAwCiAgICApOwogICAgdGVzdF9zdGVwcygkYXJyLCAiT1AncyIsICRyZXBldGl0aW9ucyk7CiAKICAgICRwb3B1bGF0aW9uID0gYXJyYXkoCiAgICAJIldhc2hpbmd0b24iID0+IDY1OC44OTMsICAvLyB0aG91c2FuZHMKICAgICAgCSJOZXcgWW9yayIgPT4gODQwNiwKICAgICAgCSJDaGljYWdvIiA9PiAyNzE5LAogICAgICAJIlBhc2FkZW5hIiA9PiAxMzkuNzMxLAogICAgICAJIlNlYXR0bGUiID0+IDY1Mi40MDUKICAgICk7CiAgICB0ZXN0X3N0ZXBzKCRwb3B1bGF0aW9uLCAicG9wdWxhdGlvbiIsICRyZXBldGl0aW9ucyk7Cgo/Pg==