Si l'échantillonnage est avec remplacement, vous pouvez utiliser cet algorithme (implémenté ici en Python) :
import random
items = [(10, "low"),
(100, "mid"),
(890, "large")]
def weighted_sample(items, n):
total = float(sum(w for w, v in items))
i = 0
w, v = items[0]
while n:
x = total * (1 - random.random() ** (1.0 / n))
total -= x
while x > w:
x -= w
i += 1
w, v = items[i]
w -= x
yield v
n -= 1
C'est O( n + m ) où m est le nombre d'articles.
Pourquoi cela fonctionne-t-il ? Il est basé sur l'algorithme suivant :
def n_random_numbers_decreasing(v, n):
"""Like reversed(sorted(v * random() for i in range(n))),
but faster because we avoid sorting."""
while n:
v *= random.random() ** (1.0 / n)
yield v
n -= 1
La fonction weighted_sample
est juste cet algorithme fusionné avec une marche de la items
pour choisir les éléments sélectionnés par ces nombres aléatoires.
Cela fonctionne à son tour parce que la probabilité que n nombres aléatoires 0.. v seront toutes inférieures à z es P \= ( z/v ) n . Résoudre pour z et vous obtenez z \= vP 1/ n . En substituant un nombre aléatoire à P choisit le plus grand nombre avec la distribution correcte ; et nous pouvons simplement répéter le processus pour sélectionner tous les autres nombres.
Si l'échantillonnage est sans remplacement, vous pouvez placer tous les éléments dans un tas binaire, où chaque nœud met en cache le total des poids de tous les éléments dans ce sous-papier. Construire le tas est O( m ). La sélection d'un élément aléatoire dans le tas, en respectant les poids, est O(log m ). La suppression de cet élément et la mise à jour des totaux mis en cache sont également O(log m ). Vous pouvez donc choisir n en O( m + n journal m ) temps.
(Note : "poids" signifie ici que chaque fois qu'un élément est sélectionné, les autres possibilités sont choisies avec une probabilité proportionnelle à leur poids. Cela ne signifie pas que les éléments apparaissent dans la sortie avec une probabilité proportionnelle à leurs poids).
Voici une implémentation de ce système, abondamment commentée :
import random
class Node:
# Each node in the heap has a weight, value, and total weight.
# The total weight, self.tw, is self.w plus the weight of any children.
__slots__ = ['w', 'v', 'tw']
def __init__(self, w, v, tw):
self.w, self.v, self.tw = w, v, tw
def rws_heap(items):
# h is the heap. It's like a binary tree that lives in an array.
# It has a Node for each pair in `items`. h[1] is the root. Each
# other Node h[i] has a parent at h[i>>1]. Each node has up to 2
# children, h[i<<1] and h[(i<<1)+1]. To get this nice simple
# arithmetic, we have to leave h[0] vacant.
h = [None] # leave h[0] vacant
for w, v in items:
h.append(Node(w, v, w))
for i in range(len(h) - 1, 1, -1): # total up the tws
h[i>>1].tw += h[i].tw # add h[i]'s total to its parent
return h
def rws_heap_pop(h):
gas = h[1].tw * random.random() # start with a random amount of gas
i = 1 # start driving at the root
while gas >= h[i].w: # while we have enough gas to get past node i:
gas -= h[i].w # drive past node i
i <<= 1 # move to first child
if gas >= h[i].tw: # if we have enough gas:
gas -= h[i].tw # drive past first child and descendants
i += 1 # move to second child
w = h[i].w # out of gas! h[i] is the selected node.
v = h[i].v
h[i].w = 0 # make sure this node isn't chosen again
while i: # fix up total weights
h[i].tw -= w
i >>= 1
return v
def random_weighted_sample_no_replacement(items, n):
heap = rws_heap(items) # just make a heap...
for i in range(n):
yield rws_heap_pop(heap) # and pop n items off it.
2 votes
L'échantillonnage est-il avec ou sans remplacement ?
2 votes
Dans tous les cas, c'est un double de stackoverflow.com/questions/352670/
1 votes
@Jason Je demandais un moyen de convertir cette approche élégante en une approche pondérée, ce n'est pas tout à fait un double.
0 votes
Nimcap : La question à laquelle j'ai fait référence concerne la sélection aléatoire pondérée.
0 votes
Avez-vous analysé l'application d'un arbre de Fenwick à votre problème ?
0 votes
Combien de fois cette question va-t-elle être posée ?
4 votes
Échantillonnage sans remplacement avec poids de telle sorte que les poids soient proportionnels aux probabilités d'inclusion de chaque élément est loin d'être une tâche triviale, et il existe de bonnes recherches récentes à ce sujet. Voir par exemple : books.google.com.br/books/about/