Per creare un metodo che iterizza l'origine una sola volta e non è necessario allocare memoria per archiviarlo temporaneamente, si contano quanti elementi si sono iterati e si determina la probabilità che l'elemento corrente sia il risultato:
public T RandomChoice<T> (IEnumerable<T> source) {
Random rnd = new Random();
T result = default(T);
int cnt = 0;
foreach (T item in source) {
cnt++;
if (rnd.Next(cnt) == 0) {
result = item;
}
}
return result;
}
Quando si è al primo punto, la probabilità è 1/1 che dovrebbe essere usato (come quello è l'unico elemento che avete visto fino a questo punto). Quando sei al secondo elemento, la probabilità è 1/2 che dovrebbe sostituire il primo elemento e così via.
Questo, naturalmente, usare un po 'di più CPU, in quanto crea un numero casuale per ogni articolo, non solo un singolo di numeri casuali per selezionare un elemento, come dasblinkenlight sottolineato. È possibile controllare se la fonte implementa IList<T>
, come suggerito Dan Tao, e utilizzare un'implementazione che utilizza le funzionalità per ottenere la lunghezza degli elementi di raccolta e di accesso per indice:
public T RandomChoice<T> (IEnumerable<T> source) {
IList<T> list = source as IList<T>;
if (list != null) {
// use list.Count and list[] to pick an item by random
} else {
// use implementation above
}
}
Nota: Si dovrebbe prendere in considerazione l'invio del Random
istanza nel metodo. Altrimenti si otterrà lo stesso seme casuale se si chiama il metodo due volte troppo vicino nel tempo, poiché il seme viene creato dall'ora corrente.
Il risultato di una prova, raccogliendo un numero da una matrice contenente 0 - 9, 1000000 volte, per mostrare che la distribuzione dei numeri scelti non è asimmetrica:
0: 100278
1: 99519
2: 99994
3: 100327
4: 99571
5: 99731
6: 100031
7: 100429
8: 99482
9: 100638
Stai dicendo che vuoi una funzione che restituisca * esattamente ciò che Python fa *? O vuoi una funzione con * lo stesso contratto *? Cioè, saresti felice se la funzione .NET restituisse elementi diversi da quello che sarebbe Python? – AakashM
Giusto per commentare le risposte fornite, @MattHickford, forse dovresti considerare, oltre a un 'IEnumerable' includere un sovraccarico' IList' (o fare un check in 'IEnumerable' se è un' IList') in modo che tu possa evitare di dover enumerare e creare una raccolta copiata. EDIT: Potresti anche aggiungere un overload 'params' per estrarre una lista fatta in fase di compilazione:' RandomChoice ("apple", "pera", "orange") ' –
AakashM, chiedo un analogo .NET del Python funzione. Cos'è un contratto? –