63 votes

Entrée aléatoire du dictionnaire

Quel est le meilleur moyen d’obtenir une entrée aléatoire d’un dictionnaire en c #?

Il me faut un certain nombre d'objets aléatoires du fictionary à afficher sur une page, mais je ne peux pas utiliser:

 Random rand = new Random();
Dictionary< string, object> dict = GetDictionary();
return dict[rand.Next()];
 

comme les dictionnaires ne sont pas accessibles par index.

Aucune suggestion?

70voto

Timothy Carter Points 7079

Si vous utilisez .net 3.5, Enumerable a une méthode d'extension ElementAt qui vous permettrait de faire:

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

55voto

StriplingWarrior Points 56276

Mis à jour pour utiliser les génériques, être encore plus rapide, et avec une explication de pourquoi cette option est plus rapide.

Cette réponse est similaire pour les autres réponses, mais puisque vous avez dit que vous avez besoin "d'un certain nombre d'éléments aléatoires" ce sera plus performant:

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

Vous pouvez utiliser cette méthode comme suit:

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

Cela a des améliorations de performances sur les autres réponses (y compris les yshuditelu de réponse).

  1. Il n'a pas à créer une nouvelle collection de tous les éléments du dictionnaire chaque fois que vous voulez vous procurer une nouvelle valeur aléatoire. C'est vraiment une grosse affaire si votre dictionnaire a beaucoup d'éléments.
  2. Il n'a pas à effectuer une recherche basée sur le Dictionnaire de la clé à chaque fois que vous récupérer une valeur aléatoire. Pas aussi grand que #1, mais c'est encore plus de deux fois plus vite de cette façon.

Mes tests montrent qu'avec 1000 objets dans le dictionnaire, cette méthode va d'environ 70 fois plus rapide que les autres méthodes proposées.

22voto

Robert Cartaino Points 12173

De votre dictionnaire ...

 Dictionary<string, int> dict = new Dictionary<string, object>()
 

vous pouvez créer une liste complète de clés ...

 List<string> keyList = new List<string>(dict.Keys);
 

puis sélectionnez une clé aléatoire de votre liste.

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

Ensuite, retournez simplement l'objet aléatoire correspondant à cette clé.

 return dict[randomKey];
 

16voto

StriplingWarrior Points 56276

Mon autre réponse est correcte pour la question, et pourrait être utile dans de nombreux cas, comme se rouler des informations à partir des dés spéciaux (chaque dé de dés est aléatoire, indépendant de l'autre dés). Cependant, vos commentaires le faire sonner comme vous peut être l'espoir d'obtenir une série de "uniques" éléments hors de l' Dictionary, un peu comme la distribution de cartes à partir d'un pont. Une fois qu'une carte est distribuée, vous ne voulez jamais voir la même carte jusqu'à ce qu'un remaniement. Dans ce cas, la meilleure stratégie dépend exactement ce que vous faites.

Si vous obtenez seulement un certain nombre d'éléments à l'intérieur d'un vaste Dictionary, alors vous devriez être en mesure d'adapter mon autre réponse, le retrait de l'élément au hasard dans la liste à chaque fois un nouveau est extrait. Vous voudrez aussi de faire la liste dans un LinkedList, parce que même si ça va être plus lente pour rechercher un élément par son indice, c'est beaucoup moins cher pour supprimer des éléments dans le milieu. Le code pour que cela serait un peu plus compliqué, donc si vous êtes prêt à sacrifier un peu de performance pour plus de simplicité, vous pourriez faire ceci:

public IEnumerable<TValue> UniqueRandomValues<TKey, TValue>(IDictionary<TKey, TValue> dict)
{
    Random rand = new Random();
    Dictionary<TKey, TValue> values = new Dictionary<TKey, TValue>(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;
    }
}

Si, d'autre part, vous avez l'intention de tirer un nombre important d'éléments à partir de votre dictionnaire (c'est à dire de traiter plus de log(n) de votre "deck"), vous serez mieux brouiller l'ensemble de votre deck, puis en la tirant par le haut:

public IEnumerable<TValue> UniqueRandomValues<TKey, TValue>(IDictionary<TKey, TValue> dict)
{
    // Put the values in random order
    Random rand = new Random();
    LinkedList<TValue> values = new LinkedList<TValue>(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();
    }
}

Le crédit va à ookii.org pour la simple brassage de code. Si ce n'est toujours pas tout à fait à ce que vous recherchez, vous pouvez peut-être commencer une nouvelle question avec plus de détails au sujet de ce que vous essayez de faire.

4voto

Henk Holterman Points 153608

Quelque chose comme:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X