Wie greife ich auf zufällige Elemente in der Liste zu?


233

Ich habe eine ArrayList und muss in der Lage sein, auf eine Schaltfläche zu klicken und dann zufällig eine Zeichenfolge aus dieser Liste auszuwählen und in einer Nachrichtenbox anzuzeigen.

Wie würde ich das machen?

Antworten:


403
  1. Erstellen Sie Randomirgendwo eine Instanz der Klasse. Beachten Sie, dass es ziemlich wichtig ist, nicht jedes Mal eine neue Instanz zu erstellen, wenn Sie eine Zufallszahl benötigen. Sie sollten die alte Instanz wiederverwenden, um eine Einheitlichkeit der generierten Zahlen zu erreichen. Sie können staticirgendwo ein Feld haben (seien Sie vorsichtig bei Thread-Sicherheitsproblemen):

    static Random rnd = new Random();
  2. Bitten Sie die RandomInstanz, eine Zufallszahl mit dem Maximum der Anzahl der Elemente in ArrayList:

    int r = rnd.Next(list.Count);
  3. Zeigen Sie die Zeichenfolge an:

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

Gibt es eine gute Möglichkeit, dies so zu ändern, dass eine Zahl nicht wiederholt wird? Angenommen, ich wollte ein Kartenspiel durch zufällige Auswahl mischen, kann aber natürlich nicht zweimal dieselbe Karte auswählen.
AdamMc331

7
@ McAdam331 Nachschlagen Fisher-Yates Shuffle-Algorithmus: en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mehrdad Afshari

2
Sollte dies "rnd.Next (list.Count-1)" anstelle von "rnd.Next (list.Count)" sein, um den Zugriff auf das Element max zu vermeiden, das über den vermutlich 0-basierten Index hinausgeht?
B. Clay Shannon

22
@ B.ClayShannon Nein. Die Obergrenze des Next(max)Anrufs ist exklusiv.
Mehrdad Afshari

1
Was ist, wenn die Liste leer ist?
Tsu1980

137

Normalerweise verwende ich diese kleine Sammlung von Erweiterungsmethoden:

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());
    }
}

Bei einer stark typisierten Liste können Sie Folgendes schreiben:

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

Wenn Sie nur eine ArrayList haben, können Sie diese umwandeln:

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

Was ist die Komplexität von denen? Bedeutet die Faulheit von IEnumerable, dass es nicht O (N) ist?
Dave Hillier

17
Diese Antwort mischt die Liste jedes Mal neu, wenn Sie eine Zufallszahl auswählen. Es wäre viel effizienter, einen zufälligen Indexwert zurückzugeben, insbesondere bei großen Listen. Verwenden Sie dies in PickRandom - return list[rnd.Next(list.Count)];
Swax

Dies mischt nicht die ursprüngliche Liste, sondern auf einer anderen Liste, die möglicherweise immer noch nicht gut für die Effizienz ist, wenn die Liste groß genug ist.
Nawfal

.OrderBy (.) Erstellt keine weitere Liste - Es wird ein Objekt vom Typ IEnumerable <T> erstellt, das die ursprüngliche Liste in geordneter Weise durchläuft.
Johan Tidén

5
Der GUID-Generierungsalgorithmus ist unvorhersehbar, aber nicht zufällig. Ziehen Sie Randomstattdessen in Betracht, eine Instanz von im statischen Zustand zu halten.
Dai

90

Du kannst tun:

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

Wunderschönen. In ASP.NET MVC 4.5 musste ich unter Verwendung einer Liste Folgendes ändern: list.OrderBy (x => Guid.NewGuid ()). FirstOrDefault ();
Andy Brown

3
In den meisten Fällen spielt dies keine Rolle, aber dies ist wahrscheinlich viel langsamer als die Verwendung von rnd.Next. OTOH funktioniert es mit IEnumerable <T>, nicht nur mit Listen.
löslicher Fisch

12
Ich bin mir nicht sicher, wie zufällig das ist. Guids sind einzigartig, nicht zufällig.
Pomber

1
Ich denke, eine bessere und erweiterte Version dieser Antwort und des Kommentars von @ solublefish ist in dieser Antwort (plus meinem Kommentar ) zu einer ähnlichen Frage gut zusammengefasst.
Neo

23

Erstellen Sie eine RandomInstanz:

Random rnd = new Random();

Holen Sie sich eine zufällige Zeichenfolge:

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

Denken Sie jedoch daran, dass Sie das RandomObjekt wiederverwenden sollten, wenn Sie dies häufig tun . Fügen Sie es als statisches Feld in die Klasse ein, sodass es nur einmal initialisiert wird, und greifen Sie dann darauf zu.


20

Oder eine einfache Erweiterungsklasse wie diese:

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)];
    }
}

Dann rufen Sie einfach an:

myList.RandomElement();

Funktioniert auch für Arrays.

Ich würde es vermeiden, anzurufen, OrderBy()da dies für größere Sammlungen teuer sein kann. Verwenden Sie List<T>zu diesem Zweck indizierte Sammlungen wie oder Arrays.


3
Arrays in .NET sind bereits implementiert, IListsodass die zweite Überlastung nicht erforderlich ist.
Dai

3

Warum nicht:

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
Während dieses Code-Snippet die Frage lösen kann, hilft das Hinzufügen einer Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
gunr2171

3
Ich würde sagen, dass der maxValueParameter der Methode Nextnur eine Anzahl von Elementen in einer Liste sein sollte, nicht minus eins, da laut einer Dokumentation " maxValue die ausschließliche Obergrenze der Zufallszahl ist ".
David Ferenczy Rogožan

1

Ich benutze diese ExtensionMethod seit einer Weile:

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;
}

Sie hatten vergessen, zufällige Klasseninstanz hinzuzufügen
bafsar

1

Ich werde anderen Ansatz vorschlagen, wenn die Reihenfolge der Elemente in der Liste bei der Extraktion nicht wichtig ist (und jedes Element nur einmal gewählt werden), dann anstelle eines Listkann man ein verwenden , ConcurrentBagdie eine Thread-sicher ist, ungeordnete Sammlung von Objekte:

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

Der EventHandler:

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

Der TryTakeversucht, ein "zufälliges" Objekt aus der ungeordneten Sammlung zu extrahieren.


0

Ich brauchte mehr Artikel als nur einen. Also schrieb ich Folgendes:

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;
}

Mit dieser Option können Sie beliebig viele Elemente abrufen:

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

var randomItemList = _allItems.GetSelectedRandom(10); 

0

Drucken eines zufälligen Ländernamens aus einer JSON-Datei.
Modell:

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

Implementierung:

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);

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.