Inserimento casuale dal dizionario

Qual è il modo migliore per ottenere una voce casuale da un dizionario in c #?

Ho bisogno di ottenere un numero di oggetti casuali dal dizionario da visualizzare su una pagina, tuttavia non posso usare quanto segue poiché i dizionari non sono accessibili per indice:

Random rand = new Random(); Dictionary dict = GetDictionary(); return dict[rand.Next()]; 

Eventuali suggerimenti?

Aggiornato per utilizzare i generici, essere ancora più veloce e con una spiegazione del motivo per cui questa opzione è più veloce.

Questa risposta è simile alle altre risposte, ma dal momento che hai detto che hai bisogno di “un numero di elementi casuali”, questo sarà più performante:

 public IEnumerable RandomValues(IDictionary dict) { Random rand = new Random(); List values = Enumerable.ToList(dict.Values); int size = dict.Count; while(true) { yield return values[rand.Next(size)]; } } 

Puoi usare questo metodo in questo modo:

 Dictionary dict = GetDictionary(); foreach (object value in RandomValues(dict).Take(10)) { Console.WriteLine(value); } 

Questo ha miglioramenti delle prestazioni rispetto alle altre risposte (inclusa la risposta di yshuditelu).

  1. Non è necessario creare una nuova raccolta di tutti gli elementi del dizionario ogni volta che si desidera recuperare un nuovo valore casuale. Questo è davvero un grosso problema se il tuo dizionario contiene molti elementi.
  2. Non è necessario eseguire una ricerca in base alla chiave del dizionario ogni volta che si recupera un valore casuale. Non è un grosso problema come il numero 1, ma è ancora due volte più veloce in questo modo.

I miei test mostrano che con 1000 oggetti nel dizionario, questo metodo va circa 70 volte più velocemente degli altri metodi suggeriti.

Se stai usando .net 3.5, Enumerable ha un metodo di estensione ElementAt che ti permetterebbe di fare:

 return dict.ElementAt(rand.Next(0, dict.Count)).Value; 

Dal tuo dizionario …

 Dictionary dict = new Dictionary() 

puoi creare un elenco completo di chiavi

 List keyList = new List(dict.Keys); 

e quindi selezionare una chiave casuale dal tuo elenco.

 Random rand = new Random(); string randomKey = keyList[rand.Next(keyList.Count)]; 

Quindi semplicemente restituire l’object casuale corrispondente a quella chiave.

 return dict[randomKey]; 

La mia altra risposta è corretta per la domanda, e sarebbe utile in molti casi come ottenere informazioni sui rulli da dadi personalizzati (ogni tiro di dado è casuale, indipendente dagli altri dadi). Tuttavia, i tuoi commenti fanno sembrare che potresti sperare di ottenere una serie di elementi “unici” dal Dictionary , un po ‘come trattare le carte da un mazzo. Una volta distribuita una carta, non vuoi più vedere la stessa carta fino a quando non ri-shuffle. In tal caso, la migliore strategia dipenderà esattamente da quello che stai facendo.

Se ottieni solo alcuni elementi da un Dictionary grandi dimensioni, dovresti essere in grado di adattare la mia altra risposta, rimuovendo l’elemento casuale dall’elenco ogni volta che ne viene recuperato uno nuovo. Probabilmente vorrai anche fare la lista in una LinkedList , perché anche se sarà più lento trovare un elemento dal suo indice, è molto meno costoso rimuovere gli elementi dal centro di essa. Il codice per questo sarebbe un po ‘più complicato, quindi se sei disposto a sacrificare alcune prestazioni per semplicità puoi semplicemente fare questo:

 public IEnumerable UniqueRandomValues(IDictionary dict) { Random rand = new Random(); Dictionary values = new Dictionary(dict); while(values.Count > 0) { TKey randomKey = values.Keys.ElementAt(rand.Next(0, values.Count)); // hat tip @yshuditelu TValue randomValue = values[randomKey]; values.Remove(randomKey); yield return randomValue; } } 

Se, d’altra parte, hai intenzione di estrarre un numero significativo di elementi dal tuo dizionario (cioè distribuire più del log (n) del tuo “mazzo”), starai meglio a mischiare l’intero mazzo prima e poi tirando dall’alto:

 public IEnumerable UniqueRandomValues(IDictionary dict) { // Put the values in random order Random rand = new Random(); LinkedList values = new LinkedList(from v in dict.Values orderby rand.Next() select v); // Remove the values one at a time while(values.Count > 0) { yield return values.Last.Value; values.RemoveLast(); } } 

Il merito va a ookii.org per il semplice codice di mescolamento. Se questo non è ancora quello che stavi cercando, forse puoi iniziare una nuova domanda con maggiori dettagli su ciò che stai cercando di fare.

Qualcosa di simile a:

 Random rand = new Random(); Dictionary dict = GetDictionary(); var k = dict.Keys.ToList()[rand.Next(dict.Count)]; return dict[k]; 

Questo non sarà terribilmente veloce, ma dovrebbe funzionare:

 Random rand = new Random(); Dictionary dict = GetDictionary(); return dict.Skip(rand.Next(dict.Count)).First().Value; 

Una soluzione semplice sarebbe utilizzare il metodo di estensione ToList() e utilizzare l’indice della lista.

Se hai solo bisogno dei valori o delle chiavi (non della coppia chiave / valore) restituisci queste raccolte dal dizionario e usa ToList() .

  Random rand = new Random(); Dictionary dict = GetDictionary(); var k = dict.ToList()[rand.Next(dict.Count)]; // var k = dict.Values.ToList()[rand.Next(dict.Count)]; // var k = dict.Keys.ToList()[rand.Next(dict.Count)]; Console.WriteLine("Random dict pair {0} = {1}", k.Key, k.Value); 

Credo che l’unico modo sia creare prima un elenco separato di KeyValuePairs.

 public static class DictionaryExtensions { public static TKey[] Shuffle( this System.Collections.Generic.Dictionary source) { Random r = new Random(); TKey[] wviTKey = new TKey[source.Count]; source.Keys.CopyTo(wviTKey, 0); for (int i = wviTKey.Length; i > 1; i--) { int k = r.Next(i); TKey temp = wviTKey[k]; wviTKey[k] = wviTKey[i - 1]; wviTKey[i - 1] = temp; } return wviTKey; } } 

Campione

  // Using System.Collections.Generic.Dictionary myDictionary = new System.Collections.Generic.Dictionary(); // myDictionary.Add(myObjectKey1, myObjectValue1); // Sample // myDictionary.Add(myObjectKey2, myObjectValue2); // Sample // myDictionary.Add(myObjectKey3, myObjectValue3); // Sample // myDictionary.Add(myObjectKey4, myObjectValue4); // Sample // var myShufledKeys = myDictionary.Shuffle(); // Sample // var myShufledValue = myDictionary[myShufledKeys[0]]; // Sample // Easy Sample var myObjects = System.Linq.Enumerable.Range(0, 4); foreach(int i in myObjects) myDictionary.Add(i, string.Format("myValueObjectNumber: {0}", i)); var myShufledKeys = myDictionary.Shuffle(); var myShufledValue = myDictionary[myShufledKeys[0]];