Подтвердить что ты не робот

Создание случайных результатов по весу в PHP?

Я знаю, как генерировать случайное число в PHP, но позволяет сказать, что я хочу случайное число между 1-10, но я хочу больше 3,4,5, а затем 8,9,10. Как это возможно? Я бы опубликовал то, что я пробовал, но честно, я даже не знаю, с чего начать.

4b9b3361

Ответ 1

Основываясь на @Allain answer/ссылка, я работал над этой быстрой функцией в 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;
      }
    }
  }

Ответ 2

Для эффективного случайного числа, скошенного последовательно к одному концу шкалы:

  • Выберите непрерывное случайное число между 0..1
  • Поднимитесь на степень γ, чтобы ее уклонить. 1 невзвешен, нижний дает больше более высоких чисел и наоборот
  • Масштабирование до требуемого диапазона и округление до целого

например. в PHP (непроверенный):

function weightedrand($min, $max, $gamma) {
    $offset= $max-$min+1;
    return floor($min+pow(lcg_value(), $gamma)*$offset);
}
echo(weightedrand(1, 10, 1.5));

Ответ 3

Там неплохой учебник для вас.

В принципе:

  • Суммируйте вес всех чисел.
  • Выберите случайное число меньше
  • вычесть веса по порядку, пока результат не станет отрицательным, и верните это число, если оно есть.

Ответ 4

Наивным взломом для этого было бы создание списка или массива типа

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

И затем произвольно выберите из него.

Ответ 5

Этот учебник позволяет вам пройти через него, на 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]; 
}

Ответ 6

Просто и справедливо. Просто скопируйте/вставьте и протестируйте его.

/**
 * 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 />';
}

Изменить: добавлена ​​отсутствующая скобка в конце.

Ответ 7

Вы можете использовать weightedChoice из Нестандартная библиотека PHP. Он принимает список пар (item, weight), чтобы иметь возможность работать с элементами, которые не могут быть массивами. Вы можете использовать pairs для преобразования 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.

Ответ 8

Поскольку я использовал решение 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>

Ответ 9

/**
 * @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 для массива. И у меня есть случайное значение;).

Ответ 10

Я просто выпустил класс чтобы легко выполнять взвешенную сортировку.

Он основан на том же алгоритме, который указан в ответах Brad's и Allain's, и оптимизирован для скорости, модульное тестирование для равномерного распределения и поддерживает элементы любого типа 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();

Вероятность получения одного из элементов зависит от его связанного веса. Единственное ограничение состоит в том, что веса должны быть целыми числами.

Ответ 11

функция 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;

}

Я изменил это из ответа здесь Выбор случайного элемента по пользовательским весам

После того, как я написал это, я увидел, что у кого-то еще был более элегантный ответ. Он он он.

Ответ 12

Многие ответы на этой странице, похоже, используют взлетарение массива, чрезмерную итерацию, библиотеку или трудный для чтения процесс. Конечно, все думают, что их собственный ребенок самый симпатичный, но я честно считаю, что мой подход скуден, прост и легко читается/изменяется...

В 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!

ЭТО ПОДХОД НЕ НУЖДАЕТСЯ, ЧТОБЫ ИТЕРАТЬ ВСЮ СТРАНИЦУ