2012-05-11 3 views
3

Предположим, что я хочу choose номер от 1-10 в случайном порядке, но есть весы для каждого номера.PHP Выберите случайное число с весами

1 - 15% chance 
2 - 15% chance 
3 - 12% chance 
4 - 12% chance 
5 - 10% chance 
6 - 10% chance 
7 - 8% chance 
8 - 8% chance 
9 - 5% chance 
10 - 5% chance 

Как я могу это сделать в PHP?

+0

Можно использовать стандартное распределение (гауссово) алгоритм со средним значением в 1 , но ответ ChristopheD намного проще. – Vulcan

ответ

4

Предполагаю, что ваши проценты составляют до 100%?

Построить массив с

15 times a '1' value, 
15 times a '2' value, 
... 
10 times a '6' value, 
8 times a '7' value, 
... 
5 times 1 '10' value 

Вы будете в конечном итоге с одного массива, который содержит 100 элементов.

Выберите элемент случайным образом (и вытащите его из массива).

+0

Есть ли математический способ сделать это, хотя без накладных расходов массива? Как бы мы закодировали то, что было от 1-100 000. – Justin

+0

это отлично работает, если все ваши весы являются целыми числами – Mala

+0

, что делает массив таким, как кажется, чрезмерным, и он не будет поддерживать вес, как 10.5% – Yamiko

0

Поместите их все несколько раз в массив, например. 1 15 раз, 3 12 раз и так далее. Затем выберите случайное число из этого массива.

$array = array_merge (array_fill (0, 15, 1), array_fill (0, 15, 2), array_fill (0, 12, 3), array_fill (0, 12, 4), array_fill (0, 10, 5), array_fill (0, 10, 6), array_fill (0, 8, 7), array_fill (0, 8, 8), array_fill (0, 5, 9), array_fill (0, 5, 10)); 
$random_number = array_rand ($array); 
1

Если ваши веса в процентах, выбрать случайное число в диапазоне от 0 до 100, а затем итеративно вычесть проценты, пока не пересечет нуль:

<?php 
function getWeightedRandom() { 
    $weights = array(15, 15, 12, ...); // these should add up to 100 
    $r = rand(0, 99); 
    for ($i=0; $i<count($weights); $i++) { 
     $r -= $weights[$i]; 
     if ($r < 0) 
      return $i+1; 
    } 
} 
?> 

Это имеет дополнительное преимущество поддержки Нецелочисленных веса.

+0

Не работает, если значения имеют равные веса, представьте $ weight как массив 10 раз 10. Вы всегда получите первое значение. Также зависит от сортировки $ weight (по убыванию). – ccKep

+0

На самом деле он работает с равными взвешенными значениями, $ wights не нужно сортировать (диапазон [0 - 0.2] точно так же, как диапазон [0,8 - 1]) ... – Mala

+0

Да, но вы возвращаетесь к первому значению, которое соответствует $ r <0. Представьте себе '$ weights = array (1, 90, 9);'. Ваша петля выполняет итерацию 1 как первый элемент, и у нее есть только 2% -ный шанс ** NOT ** возврата. ($ r = 0 или $ r = 1) (например, 98% времени, когда у вас был бы ключ с вероятностью 1% в этом примере) – ccKep

1

пример вторя значение с весом OPS с ниже класса:

echo 1+Rand::get_weighted_rand(array(15,15,12,12,10,10,8,8,5,5));

и класс:

class Rand 
{ 
    /* 
    * generates a random value based on weight 
    * @RETURN MIXED: returns the key of an array element 
    * @PARAM $a ARRAY: 
    * the array key is the value returned and the array value is the weight 
    *  if the values sum up to less than 100 than the last element of the array 
    *  is the default value when the number is out of the range of other values 
    * @PARAM $p INT: number of digits after decimal 
    * 
    * i.e array(1=>20, 'foo'=>80): has an 80 chance of returning Foo 
    * i.e array('bar'=>0.5, 2=>1, 'default'=>0), 1: 98.5% chance of returning default 
    */ 
    public static function get_weighted_rand($a, $p=0) 
    { 
     if(array_sum($a)>100) 
      return FALSE;#total must be less than 100 
     $p=pow(10, $p+2); 
     $n=mt_rand(1,$p)*(100/$p); 
     $range=100; 
     foreach($a as $k=>$v) 
     { 
      $range-=$v; 
      if($n>$range) 
       return $k; 
     } 
      #returning default value 
     end($a); 
     return key($a); 
    } 
} 
Смежные вопросы