2011-01-18 6 views
6

Можно создать дубликат:
Generating random results by weight in PHP?Выбор случайного элемента с помощью определяемых пользователем весов

У меня есть веб-приложение, где пользователи могут добавлять 1-20 строк текста и присвоить вес к ним как часто это должно проявляться. Затем система выбирает случайную строку, основанную на определенных весах. Каков наилучший способ сделать это? Значения диапазона для веса каждой строки? Могу ли я просто назначить пользователю номер (0-100) для каждой строки? Как бы вы выбрали случайную строку? (Каждый выбор не беспокоится о том, что было выбрано раньше, каждая строка имеет одинаковые шансы (по весу) выбора в начале каждого вызова).

+1

Связанный: http://stackoverflow.com/questions/4463561/weighed-random-selection-from-array/4463613#4463613 –

+1

Этот вопрос возникает часто. Вот хороший с простым ответом: http://stackoverflow.com/questions/445235/generating-random-results-by-weight-in-php –

ответ

6

Я использую эту функцию в нескольких PHP игровых движков:

<?php 
/** 
* @param array $values - just the weights 
* @return integer A number between 0 and count($values) - 1 
*/ 
function getBucketFromWeights($values) { 
    $total = $currentTotal = $bucket = 0; 
    $firstRand = mt_rand(1, 100); 

    foreach ($values as $amount) { 
     $total += $amount; 
    } 

    $rand = ($firstRand/100) * $total; 

    foreach ($values as $amount) { 
     $currentTotal += $amount; 

     if ($rand > $currentTotal) { 
      $bucket++; 
     } 
     else { 
      break; 
     } 
    } 

    return $bucket; 
} 

Использование

Предположим, у меня есть веса пользователя в ассоциативном массиве, где каждая строка указывает на его вес:

$weighted_strings = array(
    "important string" => 100, 
    "terrible string" => 10, 
    "never string" => 0, 
    // etc 
); 

Если бы я хотел натянуть нитку по весу, я бы сделал это:

$weights = array_values($weighted_strings); 
$strings = array_keys($weighted_strings); 
$index = getBucketFromWeights($weights); 
$selectedString = $strings[$index]; 
+2

Это может быть дополнительно оптимизировано, если вы создадите обратный ассоциативный массив, где ключи представляют собой суммы весов до сих пор, а значения - это строки, поэтому примерно так: '0 =>« важная строка », 100 =>« ужасная строка », 110 =>« никогда не строка », это позволяет вам найдите выбранный элемент, используя двоичный поиск. Конечно, для всего лишь нескольких элементов это не стоит усилий. – biziclop

+0

@biziclop Если вы собираетесь выполнять двоичный поиск, нет никакой пользы в использовании ассоциативного массива вместо отсортированного списка. –

+0

Код он работает, но я не могу понять логику. Можете ли вы объяснить мне, как именно это работает. Я имею в виду, например, какова цель этой строки: $ rand = ($ firstRand/100) * $ total; –

1

Вот простая реализация:

function Probability($data, $number = 1) 
{ 
    $result = array(); 

    if (is_array($data) === true) 
    { 
     $data = array_map('abs', $data); 
     $number = min(max(1, abs($number)), count($data)); 

     while ($number-- > 0) 
     { 
      $chance = 0; 
      $probability = mt_rand(1, array_sum($data)); 

      foreach ($data as $key => $value) 
      { 
       $chance += $value; 

       if ($chance >= $probability) 
       { 
        $result[] = $key; unset($data[$key]); break; 
       } 
      } 
     } 
    } 

    return $result; 
} 

С помощью этой функции вы можете указать, сколько уникальных Взвешенные случайные элементы, которые вы хотите (IDEOne).

-1

Приятный ответ предоставляется здесь, но есть способ сэкономить на петле Loockup. Более быстрый способ до select random value from array. На самом деле идея одинакова, просто работает быстрее, как простой цикл.

 Смежные вопросы

  • Нет связанных вопросов^_^