Многие ответы на этой странице, кажется, используют раздувание массива, чрезмерную итерацию, библиотеку или трудный для чтения процесс. Конечно, все думают, что их собственный ребенок - самый милый, но я, честно говоря, считаю, что мой подход прост, прост и легко читается / изменяется ...
В соответствии с 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;
}
}
Этот подход не требует модификации массива и, вероятно, не нужно будет повторять весь массив (но может).
С другой стороны, если вы собираетесь сделать более одного случайного выбора в массиве и / или ваш массив достаточно большой * (для уверенности сделайте свой собственный сравнительный анализ), реструктуризация массива может быть лучше.
Стоимость памяти для генерации нового массива будет все больше оправдываться как:
- увеличивается размер массива и
- увеличивается число случайных выборов.
Новый массив требует замены «weight» на «limit» для каждого значения путем добавления веса предыдущего элемента к весу текущего элемента.
Затем переверните массив так, чтобы ограничения были ключами массива, а значения - значениями массива.
Логика такова: выбранное значение будет иметь самый низкий предел:> = $ 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!
ЭТОМУ ПОДХОДУ НИКОГДА НЕ НУЖНО ИТЕРИРОВАТЬ ВСЮ Массив