Sai sempre il numero totale di valori nel dizionario? Se è così, questo potrebbe essere facile da fare con il seguente algoritmo, che può essere utilizzato ogni volta che si vuole fare una selezione probabilistica di alcuni articoli di un elenco ordinato:
- iterare l'elenco delle chiavi.
- Genera un valore casuale distribuito uniformemente tra 0 e 1 (ovvero "lancia i dadi").
- Supponendo che questa chiave abbia valori N_VALS associati ad esso e ci sono valori totali TOTAL_VALS nell'intero dizionario, accettare questa chiave con una probabilità N_VALS/N_REMAINING, dove N_REMAINING è il numero di elementi rimasti nell'elenco.
Questo algoritmo ha il vantaggio di non dover generare alcun nuovo elenco, che è importante se il dizionario è grande. Il tuo programma paga solo il ciclo sui tasti K per calcolare il totale, un altro ciclo sui tasti che termineranno in media a metà e qualunque sia il costo per generare un numero casuale compreso tra 0 e 1. La generazione di un numero casuale è un'applicazione molto comune nella programmazione, quindi molte lingue hanno un'implementazione rapida di tale funzione. In Python l'random number generator un'implementazione C di Mersenne Twister algorithm, che dovrebbe essere molto veloce. Inoltre, la documentazione afferma che questa implementazione è thread-safe.
Ecco il codice.Sono sicuro che si può pulire in su se si desidera utilizzare le funzionalità più divinatorio:
#!/usr/bin/python
import random
def select_weighted(d):
# calculate total
total = 0
for key in d:
total = total + len(d[key])
accept_prob = float(1.0/total)
# pick a weighted value from d
n_seen = 0
for key in d:
current_key = key
for val in d[key]:
dice_roll = random.random()
accept_prob = float(1.0/(total - n_seen))
n_seen = n_seen + 1
if dice_roll <= accept_prob:
return current_key
dict = {
'a': [1, 3, 2],
'b': [6],
'c': [0, 0]
}
counts = {}
for key in dict:
counts[key] = 0
for s in range(1,100000):
k = select_weighted(dict)
counts[k] = counts[k] + 1
print counts
Dopo l'esecuzione di questo 100 volte, ottengo tasti di selezione di questo numero di volte:
{'a': 49801, 'c': 33548, 'b': 16650}
quelli sono abbastanza vicino ai vostri valori attesi di:
{'a': 0.5, 'c': 0.33333333333333331, 'b': 0.16666666666666666}
Edit: Miles ha sottolineato un grave errore nella mia implementazione originale, che da allora è stato corretto. Mi dispiace per quello!
Possibile duplicato di [Scelta ponderata breve e semplice] (http://stackoverflow.com/questions/10803135/weighted-choice-short-and-simple) –