Je sais comment générer un nombre aléatoire dans PHP, mais disons que je veux un nombre aléatoire compris entre 1 et 10 mais que je veux davantage de 3,4,5 puis 8,9,10. Comment est-ce possible? Je publierais ce que j'ai essayé, mais honnêtement, je ne sais même pas par où commencer.
Basé sur @/Allain's answer / link , j'ai développé cette fonction rapide en PHP. Vous devrez le modifier si vous souhaitez utiliser une pondération non entière.
/**
* getRandomWeightedElement()
* Utility function for getting random values with weighting.
* Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50)
* An array like this means that "A" has a 5% chance of being selected, "B" 45%, and "C" 50%.
* The return value is the array key, A, B, or C in this case. Note that the values assigned
* do not have to be percentages. The values are simply relative to each other. If one value
* weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66%
* chance of being selected. Also note that weights should be integers.
*
* @param array $weightedValues
*/
function getRandomWeightedElement(array $weightedValues) {
$Rand = mt_Rand(1, (int) array_sum($weightedValues));
foreach ($weightedValues as $key => $value) {
$Rand -= $value;
if ($Rand <= 0) {
return $key;
}
}
}
Pour un nombre aléatoire efficace biaisé systématiquement vers une extrémité de l'échelle:
par exemple. dans PHP (non testé):
function weightedrand($min, $max, $gamma) {
$offset= $max-$min+1;
return floor($min+pow(lcg_value(), $gamma)*$offset);
}
echo(weightedrand(1, 10, 1.5));
Il y a un très bon tutoriel pour vous .
Fondamentalement:
Le hack naïf pour cela serait de construire une liste ou un tableau comme
1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10
Et puis sélectionnez au hasard à partir de cela.
Ce tutoriel vous le guide, en PHP, avec plusieurs solutions de copier/coller. Notez que cette routine est légèrement modifiée par rapport à ce que vous trouverez sur cette page, à la suite du commentaire ci-dessous.
Une fonction extraite du post:
/**
* weighted_random_simple()
* Pick a random item based on weights.
*
* @param array $values Array of elements to choose from
* @param array $weights An array of weights. Weight must be a positive number.
* @return mixed Selected element.
*/
function weighted_random_simple($values, $weights){
$count = count($values);
$i = 0;
$n = 0;
$num = mt_Rand(1, array_sum($weights));
while($i < $count){
$n += $weights[$i];
if($n >= $num){
break;
}
$i++;
}
return $values[$i];
}
Vous pouvez utiliser weightedChoice from Bibliothèque PHP non standard . Il accepte une liste de paires (élément, poids) pour pouvoir travailler avec des éléments qui ne peuvent pas être des clés de tableau. Vous pouvez utiliser pairs function pour convertir array(item => weight)
au format requis.
use function \nspl\a\pairs;
use function \nspl\rnd\weightedChoice;
$weights = pairs(array(
1 => 10,
2 => 15,
3 => 15,
4 => 15,
5 => 15,
6 => 10,
7 => 5,
8 => 5,
9 => 5,
10 => 5
));
$number = weightedChoice($weights);
Dans cet exemple, 2-5 apparaîtra 3 fois plus souvent que 7-10.
Clair et juste . Il suffit de copier/coller et de le tester.
/**
* Return weighted probability
* @param (array) prob=>item
* @return key
*/
function weightedRand($stream) {
$pos = mt_Rand(1,array_sum(array_keys($stream)));
$em = 0;
foreach ($stream as $k => $v) {
$em += $k;
if ($em >= $pos)
return $v;
}
}
$item['30'] = 'I have more chances than everybody :]';
$item['10'] = 'I have good chances';
$item['1'] = 'I\'m difficult to appear...';
for ($i = 1; $i <= 10; $i++) {
echo weightedRand($item).'<br />';
}
Edit: Ajout du support manquant à la fin.
Depuis que j'ai utilisé la solution de IainMH, je peux aussi bien partager mon code PHP:
<pre><?php
// Set total number of iterations
$total = 1716;
// Set array of random number
$arr = array(1, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5);
$arr2 = array(0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 5);
// Print out random numbers
for ($i=0; $i<$total; $i++){
// Pick random array index
$Rand = array_Rand($arr);
$Rand2 = array_Rand($arr2);
// Print array values
print $arr[$Rand] . "\t" . $arr2[$Rand2] . "\r\n";
}
?></pre>
fonction getBucketFromWeights ($ valeurs) { $ total = $ currentTotal = $ bucket = 0;
foreach ($values as $amount) {
$total += $amount;
}
$Rand = mt_Rand(0, $total-1);
foreach ($values as $amount) {
$currentTotal += $amount;
if ($Rand => $currentTotal) {
$bucket++;
}
else {
break;
}
}
return $bucket;
}
Je ugh modifié ceci à partir d'une réponse ici Choisir un élément aléatoire en fonction des pondérations définies par l'utilisateur
Après avoir écrit cela, j'ai vu que quelqu'un d'autre avait une réponse encore plus élégante. Il il il il.
Je viens de publier une classe pour effectuer un tri pondéré facilement.
Il est basé sur le même algorithme que celui mentionné dans Brad's et Allain's answers et est optimisé pour la vitesse, testé pour une distribution uniforme et prend en charge les éléments de tout type PHP.
Son utilisation est simple. Instanciez-le:
$picker = new Brick\Random\RandomPicker();
Ajoutez ensuite des éléments sous forme de tableau de valeurs pondérées (uniquement si vos éléments sont des chaînes ou des entiers):
$picker->addElements([
'foo' => 25,
'bar' => 50,
'baz' => 100
]);
Ou utilisez des appels individuels vers addElement()
. Cette méthode supporte tout type de valeur PHP en tant qu'éléments (chaînes, nombres, objets, ...), par opposition à l'approche tableau:
$picker->addElement($object1, $weight1);
$picker->addElement($object2, $weight2);
Ensuite, obtenez un élément aléatoire:
$element = $picker->getRandomElement();
La probabilité d'obtenir l'un des éléments dépend de son poids. La seule restriction est que les poids doivent être des entiers.
Beaucoup de réponses sur cette page semblent utiliser des ballonnements de tableaux, une itération excessive, une bibliothèque ou un processus difficile à lire. Bien sûr, tout le monde pense que son propre bébé est le plus mignon, mais honnêtement, je pense que mon approche est maigre, simple et facile à lire/à modifier ...
Conformément à l'OP, je vais créer un tableau de valeurs (déclarées en tant que clés) de 1 à 10, 3, 4 et 5 représentant le double du poids des autres valeurs (déclarées en tant que valeurs).
$values_and_weights=array(
1=>1,
2=>1,
3=>2,
4=>2,
5=>2,
6=>1,
7=>1,
8=>1,
9=>1,
10=>1
);
Si vous ne faites qu'une sélection aléatoire et/ou si votre tableau est relativement petit * (faites votre propre analyse comparative pour en être sûr), c'est probablement votre meilleur pari:
$pick=mt_Rand(1,array_sum($values_and_weights));
$x=0;
foreach($values_and_weights as $val=>$wgt){
if(($x+=$wgt)>=$pick){
echo "$val";
break;
}
}
Cette approche n'implique aucune modification du tableau et n'aura probablement pas besoin d'itérer le tableau entier (mais peut l'être).
D'autre part, si vous voulez faire plus d'une sélection aléatoire sur le tableau et/ou que votre tableau est suffisamment grand * (effectuez votre propre analyse comparative pour en être sûr), la restructuration du tableau peut s'avérer plus efficace.
Le coût en mémoire pour générer un nouveau tableau sera de plus en plus justifié comme suit:
Le nouveau tableau nécessite le remplacement de "poids" par une "limite" pour chaque valeur en ajoutant le poids de l'élément précédent au poids de l'élément actuel.
Puis retournez le tableau de sorte que les limites correspondent aux clés du tableau et que les valeurs correspondent aux valeurs du tableau . La logique est la suivante: la valeur sélectionnée aura la limite la plus basse qui est> = $ pick.
// Declare new array using array_walk one-liner:
array_walk($values_and_weights,function($v,$k)use(&$limits_and_values,&$x){$limits_and_values[$x+=$v]=$k;});
//Alternative declaration method - 4-liner, foreach() loop:
/*$x=0;
foreach($values_and_weights as $val=>$wgt){
$limits_and_values[$x+=$wgt]=$val;
}*/
var_export($limits_and_values);
Crée ce tableau:
array (
1 => 1,
2 => 2,
4 => 3,
6 => 4,
8 => 5,
9 => 6,
10 => 7,
11 => 8,
12 => 9,
13 => 10,
)
Maintenant, générez le $pick
aléatoire et sélectionnez la valeur:
// $x (from walk/loop) is the same as writing: end($limits_and_values); $x=key($limits_and_values);
$pick=mt_Rand(1,$x); // pull random integer between 1 and highest limit/key
while(!isset($limits_and_values[$pick])){++$pick;} // smallest possible loop to find key
echo $limits_and_values[$pick]; // this is your random (weighted) value
Cette approche est brillante parce que isset()
est très rapide et que le nombre maximal d'appels isset()
dans la boucle while ne peut excéder le poids le plus élevé (à ne pas confondre avec la limite) dans le tableau. Pour ce cas, itérations maximum = 2!
CETTE APPROCHE N'A JAMAIS BESOIN DE RÉITER L'ENSEMBLE DU RÉSEAU