2011-01-18 5 views
6

Possible en double:
Generating random results by weight in PHP?Picking élément aléatoire par des poids définis par l'utilisateur

J'ai une application web où les utilisateurs peuvent ajouter 1-20 chaînes de texte et d'attribuer un poids à leur de combien de fois il devrait apparaître. Le système choisit alors une chaîne aléatoire en fonction des poids définis. Quelle est la meilleure manière de s'occuper de ça? Les valeurs de plage pour le poids de chaque chaîne sont-elles importantes? Pourrais-je demander à l'utilisateur d'attribuer un numéro (0-100) à chaque chaîne? Comment iriez-vous choisir une chaîne aléatoire? (Chaque choix ne s'inquiète pas de ce qui a été choisi auparavant, chaque chaîne a les mêmes chances (en fonction du poids) d'être choisie au début de chaque appel).

+1

Connexes: http://stackoverflow.com/questions/4463561/weighed-random-selection-from-array/4463613#4463613 –

+1

Cette question revient souvent. En voici un bon avec une réponse simple: http://stackoverflow.com/questions/445235/generating-random-results-by-weight-in-php –

Répondre

6

J'utilise cette fonction dans plusieurs moteurs de jeu 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; 
} 

Utilisation

Supposons que j'ai le poids de l'utilisateur dans un tableau associatif où chacun des points de corde à son poids:

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

Si je voulais tirer une chaîne en fonction du poids, je ferais ceci:

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

Cela peut être encore optimisé si vous construisez un tableau associatif inverse où les clés sont les totaux des poids jusqu'ici et les valeurs sont les chaînes, donc quelque chose comme ceci: '0 =>" chaîne importante ", 100 =>" chaîne terrible ", 110 =>" never string "', cela vous permet de trouve l'élément sélectionné en utilisant la recherche binaire. Bien sûr, pour seulement une poignée d'éléments, cela ne vaut pas la peine. – biziclop

+0

@biziclop Si vous faites une recherche binaire, il n'y a vraiment aucun avantage à utiliser un tableau associatif au lieu d'une liste triée. –

+0

Le code fonctionne, mais je ne comprends pas la logique. Pouvez-vous m'expliquer comment cela fonctionne exactement? Je veux dire par exemple quel est le but de cette ligne: $ rand = ($ firstRand/100) * $ total; –

1

Voici une implémentation simple:

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; 
} 

Avec cette fonction, vous pouvez spécifier le nombre d'éléments aléatoires pondérés uniques que vous voulez (IDEOne).

-1

Une bonne réponse est fournie ici, mais il existe un moyen d'économiser sur la boucle de loockup. Moyen plus rapide à select random value from array. En fait Idée est la même chose, fonctionne plus vite comme une simple boucle.

Questions connexes