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?

jay_t55
la source

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]);
Mehrdad Afshari
la source
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>();
Mark Seemann
la source
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()
Felipe Fujiy Pessoto
la source
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.

Joey
la source
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.

Dave_cz
la source
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()));
}
Lucas
la source
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());
Rajesh Varma
la source
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;
}
Carlos Toledo
la source
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.

Shahar Shokrani
la source
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); 
bafsar
la source
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);
RM Shahidul Islam Shahed
la source
-3

Pourquoi pas [2]:

public static T GetRandom<T>(this List<T> list)
{
     return list[(int)(DateTime.Now.Ticks%list.Count)];
}
Хидеки Матосува
la source