Я знаю, как генерировать случайное число в PHP, но позволяет сказать, что я хочу случайное число между 1-10, но я хочу больше 3,4,5, а затем 8,9,10. Как это возможно? Я бы опубликовал то, что я пробовал, но, честно говоря, я даже не знаю, с чего начать.
Основываясь на ответе / ссылке @ Allain, я работал над этой быстрой функцией в PHP. Вам нужно будет изменить его, если вы хотите использовать нецелое взвешивание.
/** * getRandomWeightedElement() * Utility function for getting random values with weighting. * Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50) * An array like this means that "A" has a 5% chance of being selected, "B" 45%, and "C" 50%. * The return value is the array key, A, B, or C in this case. Note that the values assigned * do not have to be percentages. The values are simply relative to each other. If one value * weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66% * chance of being selected. Also note that weights should be integers. * * @param array $weightedValues */ function getRandomWeightedElement(array $weightedValues) { $rand = mt_rand(1, (int) array_sum($weightedValues)); foreach ($weightedValues as $key => $value) { $rand -= $value; if ($rand <= 0) { return $key; } } }
Для эффективного случайного числа, скошенного последовательно к одному концу шкалы:
например. в PHP (непроверенный):
function weightedrand($min, $max, $gamma) { $offset= $max-$min+1; return floor($min+pow(lcg_value(), $gamma)*$offset); } echo(weightedrand(1, 10, 1.5));
Для вас очень хороший учебник .
В основном:
Наивным взломом для этого было бы создание списка или массива вроде
1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10
И затем выберите случайным образом из этого.
В этом учебном пособии вы познакомитесь с ним, на PHP, с несколькими решениями для вырезания и вставки. Обратите внимание, что эта процедура немного изменена из того, что вы найдете на этой странице, в результате комментария ниже.
Функция, взятая из сообщения:
/** * weighted_random_simple() * Pick a random item based on weights. * * @param array $values Array of elements to choose from * @param array $weights An array of weights. Weight must be a positive number. * @return mixed Selected element. */ function weighted_random_simple($values, $weights){ $count = count($values); $i = 0; $n = 0; $num = mt_rand(1, array_sum($weights)); while($i < $count){ $n += $weights[$i]; if($n >= $num){ break; } $i++; } return $values[$i]; }
Простой и справедливый. Просто скопируйте / вставьте и протестируйте его.
/** * Return weighted probability * @param (array) prob=>item * @return key */ function weightedRand($stream) { $pos = mt_rand(1,array_sum(array_keys($stream))); $em = 0; foreach ($stream as $k => $v) { $em += $k; if ($em >= $pos) return $v; } } $item['30'] = 'I have more chances than everybody :]'; $item['10'] = 'I have good chances'; $item['1'] = 'I\'m difficult to appear...'; for ($i = 1; $i <= 10; $i++) { echo weightedRand($item).'<br />'; }
Изменить: добавлена отсутствующая скобка в конце.
Вы можете использовать weightedChoice из нестандартной библиотеки PHP . Он принимает список пар (item, weight), чтобы иметь возможность работать с элементами, которые не могут быть массивами. Вы можете использовать функцию par для преобразования array(item => weight)
в необходимый формат.
use function \nspl\a\pairs; use function \nspl\rnd\weightedChoice; $weights = pairs(array( 1 => 10, 2 => 15, 3 => 15, 4 => 15, 5 => 15, 6 => 10, 7 => 5, 8 => 5, 9 => 5, 10 => 5 )); $number = weightedChoice($weights);
В этом примере 2-5 появится в 3 раза чаще, чем 7-10.
Поскольку я использовал решение IainMH, я могу также поделиться своим PHP-кодом:
<pre><?php // Set total number of iterations $total = 1716; // Set array of random number $arr = array(1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5); $arr2 = array(0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 5); // Print out random numbers for ($i=0; $i<$total; $i++){ // Pick random array index $rand = array_rand($arr); $rand2 = array_rand($arr2); // Print array values print $arr[$rand] . "\t" . $arr2[$rand2] . "\r\n"; } ?></pre>
/** * @param array $weightedValues * @return string */ function getRandomWeightedElement(array $weightedValues) { $array = array(); foreach ($weightedValues as $key => $weight) { $array = array_merge(array_fill(0, $weight, $key), $array); } return $array[array_rand($array)]; }
getRandomWeightedElement(array('A'=>10, 'B'=>90));
Это очень простой способ. Как получить случайный взвешенный элемент. Я заполняю переменную переменной $ key. Я получаю $ key для массива $ weight x. После этого используйте array_rand для массива. И у меня есть случайное значение;).
Я просто выпустил класс, чтобы легко выполнять взвешенную сортировку .
Он основан на том же алгоритме, который упоминается в ответах Брэда и Аллена , и оптимизирован для скорости, модульной проверки для равномерного распределения и поддерживает элементы любого типа PHP.
Использование его просто. Создайте его:
$picker = new Brick\Random\RandomPicker();
Затем добавьте элементы как массив взвешенных значений (только если ваши элементы являются строками или целыми):
$picker->addElements([ 'foo' => 25, 'bar' => 50, 'baz' => 100 ]);
Или используйте индивидуальные вызовы addElement()
. Этот метод поддерживает любые значения PHP как элементы (строки, числа, объекты, …), в отличие от подхода массива:
$picker->addElement($object1, $weight1); $picker->addElement($object2, $weight2);
Затем получим случайный элемент:
$element = $picker->getRandomElement();
Вероятность получения одного из элементов зависит от его связанного веса. Единственное ограничение состоит в том, что веса должны быть целыми числами.
function getBucketFromWeights ($ values) {$ total = $ currentTotal = $ bucket = 0;
foreach ($values as $amount) { $total += $amount; } $rand = mt_rand(0, $total-1); foreach ($values as $amount) { $currentTotal += $amount; if ($rand => $currentTotal) { $bucket++; } else { break; } } return $bucket;
}
Я изменил это из ответа здесь. Выбор случайного элемента по пользовательским весам
После того, как я написал это, я увидел, что у кого-то еще был более элегантный ответ. Он он он.
Многие ответы на этой странице, похоже, используют раздувание массива, чрезмерную итерацию, библиотеку или сложный процесс чтения. Конечно, все думают, что их собственный ребенок самый симпатичный, но я честно считаю, что мой подход скуден, прост и легко читается / изменяется …
На OP я создам массив значений (объявленных как ключи) от 1 до 10, с 3, 4 и 5, имеющих двойной вес других значений (объявленных как значения).
$values_and_weights=array( 1=>1, 2=>1, 3=>2, 4=>2, 5=>2, 6=>1, 7=>1, 8=>1, 9=>1, 10=>1 );
Если вы собираетесь сделать только один случайный выбор и / или ваш массив относительно невелик * (сделайте свой собственный бенчмаркинг, чтобы быть уверенным), это, вероятно, лучший выбор:
$pick=mt_rand(1,array_sum($values_and_weights)); $x=0; foreach($values_and_weights as $val=>$wgt){ if(($x+=$wgt)>=$pick){ echo "$val"; break; } }
Этот подход не включает модификацию массива и, вероятно, не потребуется итерировать весь массив (но может).
С другой стороны, если вы собираетесь сделать более одного случайного выбора в массиве и / или ваш массив достаточно велик * (убедитесь, что ваш собственный бенчмаркинг), реструктуризация массива может быть лучше.
Стоимость памяти для генерации нового массива будет все более оправдана как:
Новый массив требует замены «веса» на «предел» для каждого значения, добавляя вес предыдущего элемента к весу текущего элемента.
Затем переверните массив так, чтобы лимитами были ключи массива, а значения – значения массива. Логика: выбранное значение будет иметь самый низкий предел, который равен> = $ pick.
// Declare new array using array_walk one-liner: array_walk($values_and_weights,function($v,$k)use(&$limits_and_values,&$x){$limits_and_values[$x+=$v]=$k;}); //Alternative declaration method - 4-liner, foreach() loop: /*$x=0; foreach($values_and_weights as $val=>$wgt){ $limits_and_values[$x+=$wgt]=$val; }*/ var_export($limits_and_values);
Создает этот массив:
array ( 1 => 1, 2 => 2, 4 => 3, 6 => 4, 8 => 5, 9 => 6, 10 => 7, 11 => 8, 12 => 9, 13 => 10, )
Теперь, чтобы сгенерировать случайный $pick
и выбрать значение:
// $x (from walk/loop) is the same as writing: end($limits_and_values); $x=key($limits_and_values); $pick=mt_rand(1,$x); // pull random integer between 1 and highest limit/key while(!isset($limits_and_values[$pick])){++$pick;} // smallest possible loop to find key echo $limits_and_values[$pick]; // this is your random (weighted) value
Этот подход является блестящим, потому что isset()
работает очень быстро, и максимальное количество вызовов isset()
в цикле while может быть только до максимального веса (не путать с лимитом) в массиве. Для этого случая максимальные итерации = 2!
ЭТОТ ПОДХОД НИКОГДА НЕ НУЖДАЕТСЯ ИТЕРАЦИЮ ВСЕЙ МАССЫ