2011-01-18 17 views
6

Duplicar posibles:
Generating random results by weight in PHP?elemento de azar por pesos definidos por el usuario

Tengo una aplicación web en la que los usuarios pueden añadir 1-20 cadenas de texto y asignar un peso a ellos de con qué frecuencia debe aparecer. El sistema luego elegiría una cadena aleatoria basada en los pesos definidos. ¿Cuál es la mejor manera de hacerlo? ¿Importan los valores de rango para el peso de cada cadena? ¿Podría simplemente hacer que el usuario asigne un número (0-100) para cada cadena? ¿Cómo elegirías una cadena aleatoria? (Cada opción no se preocupa por lo que se eligió antes, cada cadena tiene las mismas probabilidades (según el peso) de ser elegida al inicio de cada llamada).

+1

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

+1

Esta pregunta aparece a menudo. Aquí hay una buena con una respuesta simple: http://stackoverflow.com/questions/445235/generating-random-results-by-weight-in-php –

Respuesta

6

utilizo esta función en varios motores de juego 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; 
} 

Uso

Supongamos que tengo los pesos de los usuarios en una matriz asociativa que cada cadena puntos a su peso:

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

Si quisiera tirar de una cuerda en función del peso, haría esto:

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

Esto se puede optimizar aún más si se construye una matriz asociativa inversa donde las claves son los totales de los pesos hasta el momento y los valores son las cadenas, por lo que algo como esto: '0 =>" cadena importante ", 100 =>" cadena terrible ", 110 =>" nunca cadena "', esto le permite encuentra el elemento seleccionado usando la búsqueda binaria. Por supuesto, solo para un puñado de elementos no vale la pena el esfuerzo. – biziclop

+0

@biziclop Si va a hacer una búsqueda binaria, no hay ningún beneficio en usar una matriz asociativa en lugar de una lista ordenada. –

+0

El código funciona, pero no puedo entender la lógica. ¿Puedes explicarme cómo funciona exactamente? Quiero decir, por ejemplo, cuál es el objetivo de esta línea: $ rand = ($ firstRand/100) * $ total; –

1

Aquí es una aplicación sencilla:

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

Con esta función, puede especificar el número de elementos aleatorios ponderados únicos que desee (IDEOne).

-1

Aquí se proporciona una buena respuesta, pero hay una forma de ahorrar en el bucle de loockup. Forma más rápida al select random value from array. En realidad, Idea es lo mismo, simplemente funciona más rápido como un simple bucle.

Cuestiones relacionadas