PHP: Как случайным образом выбрать максимальное значение вероятности? - PullRequest
5 голосов
/ 18 марта 2020

У меня есть ниже массив и код

    $a = [
      149 => 55,
      130 => 10,
      131 => 5,
      132 => 5,
      133 => 10,
      134 => 10,
      135 => 5
    ];

   $rand = rand (0,(count($a)-1));

   echo array_values($a)[$rand];

Это даст в основном результат как 5,10 вместо 55.

Итоговое значение составляет 100% вероятности. Значения могут быть в десятичном виде, например, 55,55, 10,10 и т. Д. c. но в целом будет 100%

Я уже следовал https://www.geeksforgeeks.org/how-to-get-random-value-out-of-an-array-in-php/

Но это не дает идеальный результат, как ожидалось.

То, что имеет наибольшую вероятность, должно выбираться главным образом и случайным образом.

Таким образом, результат может быть таким: 55, 55, 10, 10, 10, 55, 5 и т. Д. c ..

Я нашел несколько полезных ссылок Генерация случайных результатов по весу в PHP? , где Вероятность = Вес

Ответы [ 5 ]

9 голосов
/ 18 марта 2020

Прямо сейчас ваш массив выглядит так: -

55, 10, 5, 5, 10, 10, 5

Теперь вы должны сгенерировать случайное число между [0, 100), назовем его r.

  • Теперь, если r находится между [0, 55), выберите значение 55.
  • , если r находится между [55, 55 + 10 = 65), выберите значение 10.
  • иначе, если r лежит между [65, 65 + 5 = 70), выберите значение 5.
  • , если r находится между [70, 70 + 5 = 75), выберите значение 5.
  • иначе, если r лежит в пределах [75, 75 + 10 = 85), выберите значение 10.
  • , если r лежит в пределах [85, 85 + 10 = 95), выберите значение 10.
  • , иначе, если r лежит в пределах [95, 95 + 5 = 100), выберите значение 5.

Я уверен, что вы поняли бы идею ...

Итак, для общего случая, если у вас есть массив с именем 'arr', это псевдокод: -

function SELECTPROB()
{
    $r = generateRandomNumber(0, 100);    //function to generate random number between 0 and 100, (100 exclusive)
    $sum = 0;
    foreach($arr as $i)
    {
        if($r >= $sum && $r < $sum + $i)
        {
            return $i
        }
        $sum = $sum + $i
    }
    return -1    //Should technically never reach upto this, but it can if your probability's sum is not 100
}
1 голос
/ 23 марта 2020

Вот реализация, аналогичная выбору колеса рулетки в GA. версия ответа EReload, но ограниченная суммой, а не 100.

    $a = [
      149 => 55,
      130 => 10,
      131 => 5,
      132 => 5,
      133 => 10,
      134 => 10,
      135 => 5
    ];

   echo randSelect($a);

   function randSelect($a) {
        $values = array_values($a);
        $sum = array_sum($values);
        $rand = (rand(0,1000)/1000) * $sum;
        $partialSum = 0;

        for ($i=0; $i < count($values); $i++) {
            $partialSum += $values[$i];
            if($partialSum >= $rand){
                return $values[$i];
                // incase you are using something like array_count_values and are actually looking for the keys
                // return array_keys($a)[$i];
            }
        }
   }
0 голосов
/ 26 марта 2020

Я думаю, что вы могли бы на самом деле перемешать массив и вытолкнуть элемент, снова перемешать и вытолкнуть элемент, который будет случайным, и эти числа с большей вероятностью будут первыми.

Что вы можете сделать, это создать другой массив с 100 числами, представляющий общую вероятность и вставляющий в него количество чисел, равное его значению, в конце концов, вы перемешиваете его, чтобы позже выбрать случайный индекс. Тогда вы получите массив из 100 чисел, где наиболее повторяющееся число является наиболее вероятным. Наконец, вам просто нужно выбрать случайный индекс и создать свой массив.

Можете ли вы сказать мне, если вы ищете что-то вроде этого или я неправильно понимаю проблему

function getProb($array, $elements)
{
    $myNewArray = [];
    $myProbabilisticArray = $this->getProbabilisticArray($array);
    for ($i=0; $i < $elements; $i++) {
        $myNewArray[] = $myProbabilisticArray[array_rand($myProbabilisticArray)];
    }
    return $myNewArray;
}

function getProbabilisticArray($array) {
    $myNewArray = [];
    rsort($array);

    $currentProbability = 0;
    $accumulatedProbability = $array[0];
    $currentPosition = 0;

    while ($currentProbability < 100) {
        if ($currentProbability > $accumulatedProbability) {
            $currentPosition++;
            $accumulatedProbability += $array[$currentPosition];
        }
        array_push($myNewArray, $array[$currentPosition]);
        $currentProbability++;
    }
    shuffle($myNewArray);
    return $myNewArray;
}
0 голосов
/ 25 марта 2020

Как я понимаю, вы хотите, чтобы старшие числа появлялись чаще в методе rand независимо от того, во сколько раз меньшие числа появляются в вашем массиве. Сначала вам нужен уникальный массив.

Случайный по сумме вес является простым методом для случайного выбора, но вы можете более свободно контролировать вес по сумме мощности, а не сам по себе.

$a = [
      149 => 55,
      130 => 10,
      131 => 5,
      132 => 5,
      133 => 10,
      134 => 10,
      135 => 5
    ];

$val_arr = array_unique(array_values($a));

function rand_by_sum($arr, $power=1){
        $sum = 0;
        $f_val = function($f)use($power){
                return pow($f, $power);
        };
        foreach($arr as $f){
                $sum += $f_val($f);
        }
        $rand = mt_rand(0, $sum);

        $tmp_sum = 0;
        foreach($arr as $f){
                $tmp_sum += $f_val($f);
                if($tmp_sum >= $rand) return $f;
        }
}

for($i=0; $i< 10; $i++){
        echo rand_by_sum($val_arr, $argv[1]) . " ";
}

echo "\n";

А вот некоторые результаты теста с другим pow

php test.php 0.5
55 5 10 55 5 55 55 5 55 55 

php test.php 2
55 55 10 55 55 55 55 55 55 55 

php test.php 1
55 10 55 55 55 55 55 55 55 10

Чтобы получить значение, вы возвращаете массив как 55 => [149], затем получаете случайный результат и снова случайный в значениях обращенного массива

0 голосов
/ 24 марта 2020

В поисках ответа, который работает во всех сценариях ios или для любого числа.

и

Значения могут быть в десятичном виде, например 55,55 10.10 и др. c. но в целом будет 100%

Несмотря на то, что вы ограничиваете общий вес до 100, тот факт, что вы хотите разместить десятичные значения в этом диапазоне, означает, что вы не можете предполагать, что можно выбрать максимум 100 единиц от. Если у вас гранулярность десятых долей, то каждая единица, из которой вы можете выбрать, будет .1. Если указать до сотых долей (например, 55.55), вам понадобится относительная базовая единица .01 за раз.

Поскольку я предпочел бы не выполнять итерацию по значениям с плавающей запятой, я рекомендую вам масштабировать увеличьте все ваши значения на коэффициент, который устраняет все числа с плавающей точкой в ​​весе и генераторе случайных чисел - просто умножьте на 10/100/1000 все, что вам нужно, чтобы преобразовать все веса в целые числа.

Теперь выполните самую короткую работу итерационного процесса:

  1. L oop через ваш входной массив один раз, чтобы установить sh самую длинную десятичную точность.
  2. Выберите случайное целое число от 0 до ((сумма всех весов минус 1), умноженная на 10 на степень «самой длинной десятичной длины, представляющей собой»).
  3. L oop снова через ваш входной массив и просто проверьте, меньше ли случайное целое число чем текущий вес плюс любой предыдущий вес (ы); если нет, если это так, ломайте l oop, и поскольку выбранное взвешенное случайное число было найдено.

Код: ( Demo ) - демонстрация выполняется 10 итераций чтобы помочь выявить взвешенный эффект

$valueWeights = [
    149 => 55.555,
    130 => 10.0050,
    131 => 5,
    132 => 5.2,
    133 => 10,
    134 => 10.24,
    135 => 5
];

$mostDecimals = 0;
// not bothering to validate against infinite and extremely fringe case floats
foreach ($valueWeights as $value => $weight) {
    $tempDecimals = 0;
    while ((string)$weight !== (string)floor($weight)) {
        $weight *= 10;  // this is not permanently mutating the weight
        ++$tempDecimals;
    }
    $mostDecimals = max($mostDecimals, $tempDecimals);
}
echo "Most Decimals: {$mostDecimals}\n";
$factor = pow(10, $mostDecimals);
echo "Factor: " , $factor , "\n";
$totalWeight = (array_sum($valueWeights) - 1) * $factor;


for ($i = 0; $i < 10; ++$i) {
    $rand = mt_rand(0, $totalWeight);
    echo "\nRand: " , $rand , "\n";
    $cumulativeScaledWeight = 0;
    foreach ($valueWeights as $value => $weight) {
        $cumulativeScaledWeight += $weight * $factor;
        if ($rand < $cumulativeScaledWeight) {
            echo "Value: {$value}\n";
            break;
        }
    }
}

Вывод:

Most Decimals: 3
Factor: 1000

Rand: 52197
Value: 149

Rand: 33785
Value: 149

Rand: 4783
Value: 149

Rand: 24994
Value: 149

Rand: 76588
Value: 133

Rand: 77417
Value: 133

Rand: 40541
Value: 149

Rand: 80009
Value: 133

Rand: 14826
Value: 149

Rand: 52691
Value: 149
...