J'ai, par exemple, cette table
+ ----------------- + | des fruits | poids | + ----------------- + | pomme | 4 | | orange | 2 | | citron | 1 | + ----------------- +
J'ai besoin de retourner un fruit au hasard. Mais les pommes doivent être cueillies 4 fois plus souvent que le citron et 2 fois plus souvent que l’ orange .
Dans des cas plus généraux, cela devrait être f(weight)
souvent.
Qu'est-ce qu'un bon algorithme général pour implémenter ce comportement?
Ou peut-être y a-t-il des joyaux prêts sur Ruby? :)
Post-
scripting J'ai implémenté l'algorithme actuel dans Ruby https://github.com/fl00r/pickup
algorithms
ruby
math
random
fl00r
la source
la source
Réponses:
La solution conceptuellement la plus simple serait de créer une liste dans laquelle chaque élément apparaît autant de fois que son poids.
Ensuite, utilisez les fonctions que vous avez à votre disposition pour choisir un élément aléatoire de cette liste (par exemple, générer un index aléatoire dans la plage appropriée). Ceci n’est bien sûr pas très efficace en mémoire et nécessite des poids entiers.
Une autre approche légèrement plus compliquée ressemblerait à ceci:
Calculez les sommes cumulées de poids:
Où un indice inférieur à 4 représente une pomme , 4 à moins de 6 une orange et 6 à moins de 7 un citron .
Générez un nombre aléatoire
n
compris entre0
etsum(weights)
.n
. Le fruit correspondant est votre résultat.Cette approche nécessite un code plus compliqué que le premier, mais moins de mémoire et de calcul, et supporte les poids en virgule flottante.
Pour l’un ou l’autre algorithme, l’étape de configuration peut être effectuée une fois pour un nombre arbitraire de sélections aléatoires.
la source
Voici un algorithme (en C #) qui permet de sélectionner un élément pondéré de manière aléatoire à partir de n’importe quelle séquence, en effectuant une itération unique:
Ceci est basé sur le raisonnement suivant: sélectionnons le premier élément de notre séquence comme "résultat actuel"; ensuite, à chaque itération, conservez-le ou supprimez-le et choisissez le nouvel élément comme courant. Nous pouvons calculer la probabilité qu'un élément donné soit finalement sélectionné en tant que produit de toutes les probabilités qu'il ne soit pas ignoré lors des étapes suivantes, multiplié par la probabilité qu'il soit sélectionné en premier lieu. Si vous faites le calcul, vous verrez que ce produit se simplifie à (poids de l'élément) / (somme de tous les poids), ce qui est exactement ce dont nous avons besoin!
Etant donné que cette méthode itère une seule fois sur la séquence en entrée, elle fonctionne même avec des séquences d'une taille très grande, à condition que la somme des poids corresponde à un
int
(ou que vous puissiez choisir un type plus grand pour ce compteur).la source
Les réponses déjà présentes sont bonnes et je les développerai un peu.
Comme Benjamin a suggéré les sommes cumulatives sont généralement utilisées dans ce genre de problème:
Pour trouver un élément dans cette structure, vous pouvez utiliser quelque chose comme un morceau de code de Nevermind. Ce morceau de code C # que j'utilise habituellement:
Passons maintenant à la partie intéressante. Quelle est l'efficacité de cette approche et quelle est la solution la plus efficace? Mon morceau de code nécessite une mémoire O (n) et s'exécute en un temps O (n) . Je ne pense pas que cela puisse être fait avec moins de O (n) espace, mais la complexité temporelle peut être beaucoup plus basse, O (log n) en fait. L'astuce consiste à utiliser la recherche binaire au lieu de la boucle régulière.
Il y a aussi une histoire sur la mise à jour des poids. Dans le pire des cas, la mise à jour du poids d'un élément entraîne la mise à jour des sommes cumulatives de tous les éléments, ce qui augmente la complexité de la mise à jour à O (n) . Cela aussi peut être réduit à O (log n) en utilisant une arborescence indexée binaire .
la source
Ceci est une implémentation simple de Python:
et
Dans les algorithmes génétiques, cette procédure de sélection est appelée sélection proportionnelle à la forme physique ou sélection à la roulette depuis:
Les algorithmes classiques ont une complexité O (N) ou O (log N), mais vous pouvez également utiliser O (1) (par exemple, la sélection de la roulette via l'acceptation stochastique ).
la source
Cet esprit fait exactement ce que vous demandez.
vous pouvez l'utiliser comme ça:
Le code ci-dessus renverra très probablement (% 98) 0, qui correspond à l'index de 'pomme' pour le tableau donné.
En outre, ce code teste la méthode fournie ci-dessus:
Cela donne un résultat quelque chose comme ça:
la source