Comment accéder à un élément aléatoire dans la liste?


233

J'ai une ArrayList, et je dois pouvoir cliquer sur un bouton, puis choisir au hasard une chaîne de cette liste et l'afficher dans une boîte de message.

Comment pourrais-je procéder?

Réponses:


404
  1. Créez une instance de Randomclasse quelque part. Notez qu'il est assez important de ne pas créer une nouvelle instance chaque fois que vous avez besoin d'un nombre aléatoire. Vous devez réutiliser l'ancienne instance pour obtenir l'uniformité des nombres générés. Vous pouvez avoir un staticchamp quelque part (faites attention aux problèmes de sécurité des threads):

    static Random rnd = new Random();
  2. Demandez à l' Randominstance de vous donner un nombre aléatoire avec le maximum du nombre d'articles dans ArrayList:

    int r = rnd.Next(list.Count);
  3. Affichez la chaîne:

    MessageBox.Show((string)list[r]);

Existe-t-il un bon moyen de modifier cela afin qu'un nombre ne soit pas répété? Disons que je voulais mélanger un jeu de cartes en en sélectionnant une au hasard, mais je ne peux évidemment pas sélectionner deux fois la même carte.
AdamMc331

7
@ McAdam331 Recherchez l'algorithme Shuffle de Fisher-Yates: en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mehrdad Afshari

2
Devrait-il s'agir de "rnd.Next (list.Count-1)" au lieu de "rnd.Next (list.Count)" pour éviter d'accéder à l'élément max, qui serait au-delà de l'index présumé basé sur 0?
B. Clay Shannon

22
@ B.ClayShannon Non. La limite supérieure de l' Next(max)appel est exclusive.
Mehrdad Afshari

1
Qu'en est-il lorsque la liste est vide?
tsu1980

137

J'utilise généralement cette petite collection de méthodes d'extension:

public static class EnumerableExtension
{
    public static T PickRandom<T>(this IEnumerable<T> source)
    {
        return source.PickRandom(1).Single();
    }

    public static IEnumerable<T> PickRandom<T>(this IEnumerable<T> source, int count)
    {
        return source.Shuffle().Take(count);
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        return source.OrderBy(x => Guid.NewGuid());
    }
}

Pour une liste fortement typée, cela vous permettrait d'écrire:

var strings = new List<string>();
var randomString = strings.PickRandom();

Si tout ce que vous avez est une ArrayList, vous pouvez le lancer:

var strings = myArrayList.Cast<string>();

quelle est leur complexité? la nature paresseuse de IEnumerable signifie-t-elle que ce n'est pas O (N)?
Dave Hillier

17
Cette réponse remélange la liste à chaque fois que vous choisissez un nombre aléatoire. Il serait beaucoup plus efficace de renvoyer une valeur d'index aléatoire, en particulier pour les grandes listes. Utilisez ceci dans PickRandom - return list[rnd.Next(list.Count)];
swax

Cela ne mélange pas la liste d'origine, il le fait sur une autre liste en fait, ce qui n'est peut-être pas bon pour l'efficacité si la liste est assez grande.
nawfal

.OrderBy (.) Ne crée pas une autre liste - Il crée un objet de type IEnumerable <T> qui parcourt la liste d'origine de manière ordonnée.
Johan Tidén

5
L'algorithme de génération de GUID est imprévisible mais pas aléatoire. Envisagez Randomplutôt de conserver une instance de dans un état statique.
Dai

90

Tu peux faire:

list.OrderBy(x => Guid.NewGuid()).FirstOrDefault()

Belle. Dans ASP.NET MVC 4.5, en utilisant une liste, j'ai dû changer cela en: list.OrderBy (x => Guid.NewGuid ()). FirstOrDefault ();
Andy Brown du

3
Cela n'aura pas d'importance dans la plupart des cas, mais c'est probablement beaucoup plus lent que d'utiliser rnd.Next. OTOH cela fonctionnera sur IEnumerable <T>, pas seulement sur les listes.
solublefish

12
Je ne sais pas à quel point c'est aléatoire. Les guides sont uniques et non aléatoires.
bombardier

1
Je pense qu'une version meilleure et étendue de cette réponse et le commentaire de @ solublefish sont bien résumés dans cette réponse (plus mon commentaire ) à une question similaire.
Neo

23

Créez une Randominstance:

Random rnd = new Random();

Récupère une chaîne aléatoire:

string s = arraylist[rnd.Next(arraylist.Count)];

N'oubliez pas cependant que si vous le faites fréquemment, vous devez réutiliser l' Randomobjet. Mettez-le en tant que champ statique dans la classe afin qu'il ne soit initialisé qu'une seule fois, puis accédez-y.


20

Ou une classe d'extension simple comme celle-ci:

public static class CollectionExtension
{
    private static Random rng = new Random();

    public static T RandomElement<T>(this IList<T> list)
    {
        return list[rng.Next(list.Count)];
    }

    public static T RandomElement<T>(this T[] array)
    {
        return array[rng.Next(array.Length)];
    }
}

Ensuite, appelez simplement:

myList.RandomElement();

Fonctionne également pour les tableaux.

J'éviterais d'appeler OrderBy()car cela peut coûter cher pour les grandes collections. Utilisez des collections indexées comme List<T>ou des tableaux à cet effet.


3
Les tableaux en .NET implémentent déjà, IListdonc la deuxième surcharge n'est pas nécessaire.
Dai

3

Pourquoi pas:

public static T GetRandom<T>(this IEnumerable<T> list)
{
   return list.ElementAt(new Random(DateTime.Now.Millisecond).Next(list.Count()));
}

2
ArrayList ar = new ArrayList();
        ar.Add(1);
        ar.Add(5);
        ar.Add(25);
        ar.Add(37);
        ar.Add(6);
        ar.Add(11);
        ar.Add(35);
        Random r = new Random();
        int index = r.Next(0,ar.Count-1);
        MessageBox.Show(ar[index].ToString());

3
Bien que cet extrait de code puisse résoudre la question, y compris une explication aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondrez à la question pour les lecteurs à l'avenir, et ces personnes pourraient ne pas connaître les raisons de votre suggestion de code.
gunr2171

3
Je dirais que le maxValueparamètre de méthode Nextdevrait être juste un certain nombre d'éléments dans une liste, pas moins un, car selon une documentation " maxValue est la limite supérieure exclusive du nombre aléatoire ".
David Ferenczy Rogožan

1

J'utilise cette ExtensionMethod depuis un certain temps:

public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int count)
{
    if (count <= 0)
      yield break;
    var r = new Random();
    int limit = (count * 10);
    foreach (var item in list.OrderBy(x => r.Next(0, limit)).Take(count))
      yield return item;
}

Vous avez oublié d'ajouter une instance de classe aléatoire
bafsar

1

Je suggère une approche différente.Si l'ordre des éléments dans la liste n'est pas important lors de l'extraction (et que chaque élément ne doit être sélectionné qu'une seule fois), Listvous pouvez utiliser unConcurrentBag qui est une collection non ordonnée de thread-safe objets:

var bag = new ConcurrentBag<string>();
bag.Add("Foo");
bag.Add("Boo");
bag.Add("Zoo");

Le gestionnaire d'événements:

string result;
if (bag.TryTake(out result))
{
    MessageBox.Show(result);
}

Le TryTaketentera d'extraire un objet "aléatoire" de la collection non ordonnée.


0

J'avais besoin de plus d'articles au lieu d'un seul. J'ai donc écrit ceci:

public static TList GetSelectedRandom<TList>(this TList list, int count)
       where TList : IList, new()
{
    var r = new Random();
    var rList = new TList();
    while (count > 0 && list.Count > 0)
    {
        var n = r.Next(0, list.Count);
        var e = list[n];
        rList.Add(e);
        list.RemoveAt(n);
        count--;
    }

    return rList;
}

Avec cela, vous pouvez obtenir des éléments combien vous voulez comme au hasard comme ceci:

var _allItems = new List<TModel>()
{
    // ...
    // ...
    // ...
}

var randomItemList = _allItems.GetSelectedRandom(10); 

0

Impression aléatoire du nom du pays à partir du fichier JSON.
Modèle:

public class Country
    {
        public string Name { get; set; }
        public string Code { get; set; }
    }

Implémentation:

string filePath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\")) + @"Data\Country.json";
            string _countryJson = File.ReadAllText(filePath);
            var _country = JsonConvert.DeserializeObject<List<Country>>(_countryJson);


            int index = random.Next(_country.Count);
            Console.WriteLine(_country[index].Name);

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.