1115 Stimmen

List<T> zufällig ordnen

Wie kann man am besten die Reihenfolge einer generischen Liste in C# zufällig durcheinanderbringen? Ich habe eine endliche Menge von 75 Zahlen in einer Liste, denen ich eine zufällige Reihenfolge zuweisen möchte, um sie für eine Lotterie-Anwendung zu ziehen.

4 Stimmen

Es gibt ein offenes Problem, um diese Funktionalität in .NET zu integrieren: github.com/dotnet/corefx/issues/461

7 Stimmen

Du könntest an diesem NuGet-Paket interessiert sein, das Erweiterungsmethoden für das Mischen von IList und IEnumerable mithilfe des unten erwähnten Fisher-Yates-Algorithmus enthält

0 Stimmen

15voto

Adam Tegen Punkte 24281
    public static List Mischen(List liste)
    {
        List gemischteListe = new List();
        Random zufall = new Random();
        while (liste.Count > 0)
        {
            int index = zufall.Next(0, liste.Count); // ein zufälliges Element aus der Masterliste auswählen
            gemischteListe.Add(liste[index]); // es am Ende der gemischten Liste platzieren
            liste.RemoveAt(index);
        }
        return gemischteListe;
    }

10voto

Jodrell Punkte 32818

BEARBEITEN Der RemoveAt ist eine Schwachstelle in meiner vorherigen Version. Diese Lösung überwindet das.

public static IEnumerable Shuffle(
        this IEnumerable source,
        Random generator = null)
{
    if (generator == null)
    {
        generator = new Random();
    }

    var elements = source.ToArray();
    for (var i = elements.Length - 1; i >= 0; i--)
    {
        var swapIndex = generator.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

Beachten Sie den optionalen Random generator. Wenn die Standardimplementierung des Random in der Basisbibliothek nicht thread-sicher oder kryptografisch stark genug für Ihre Anforderungen ist, können Sie Ihre Implementierung in den Vorgang einfügen.

Eine geeignete Implementierung für eine thread-sichere kryptografisch starke Random-Implementierung finden Sie in dieser Antwort.


Hier ist eine Idee, IList auf eine (hoffentlich) effiziente Weise zu erweitern.

~~public static IEnumerable Shuffle(this IList list) { var choices = Enumerable.Range(0, list.Count).ToList(); var rng = new Random(); for(int n = choices.Count; n > 1; n--) { int k = rng.Next(n); yield return list[choices[k]]; choices.RemoveAt(k); }

    yield return list[choices[0]];
}~~

7voto

Adrian Rus Punkte 188

Man kann die Shuffle-Erweiterungsmethode aus dem MoreLinq-Paket verwenden, sie funktioniert mit IEnumerables

install-package morelinq

using MoreLinq;
...    
var randomized = list.Shuffle();

6voto

John Leidegren Punkte 57871

Dies ist meine bevorzugte Methode zum Mischen, wenn es erwünscht ist, das Original nicht zu verändern. Es handelt sich um eine Variante des Fisher-Yates "Inside-Out" Algorithmus, der auf jeder zählbaren Sequenz funktioniert (die Länge von source muss nicht von Anfang an bekannt sein).

public static IList NextList(this Random r, IEnumerable source)
{
  var list = new List();
  foreach (var item in source)
  {
    var i = r.Next(list.Count + 1);
    if (i == list.Count)
    {
      list.Add(item);
    }
    else
    {
      var temp = list[i];
      list[i] = item;
      list.Add(temp);
    }
  }
  return list;
}

Dieser Algorithmus kann auch umgesetzt werden, indem man einen Bereich von 0 bis Länge - 1 zuweist und die Indizes zufällig erschöpft, indem man den zufällig gewählten Index mit dem letzten Index vertauscht, bis alle Indizes genau einmal gewählt wurden. Der obige Code erreicht genau dasselbe, braucht aber keine zusätzliche Zuweisung. Was ziemlich praktisch ist.

In Bezug auf die Random Klasse handelt es sich um einen allgemeinen Zweck Zufallszahlengenerator (wenn ich eine Lotterie führen würde, würde ich erwägen, etwas anderes zu verwenden). Standardmäßig verwendet er auch einen auf der Zeit basierenden Startwert. Eine kleine Erleichterung des Problems besteht darin, die Random Klasse mit dem RNGCryptoServiceProvider zu starten oder Sie könnten den RNGCryptoServiceProvider in einer ähnlichen Methode wie dieser (siehe unten) verwenden, um gleichmäßig gewählte zufällige Double-Gleitkommazahlen zu generieren, doch für eine Lotterie ist es notwendig, das Konzept von Zufälligkeit und die Natur der Zufallsquellen zu verstehen.

var bytes = new byte[8];
_secureRng.GetBytes(bytes);
var v = BitConverter.ToUInt64(bytes, 0);
return (double)v / ((double)ulong.MaxValue + 1);

Der Zweck, eine zufällige Double (zwischen 0 und 1 exklusiv) zu generieren, besteht darin, sie zu einer ganzzahligen Lösung zu skalieren. Wenn Sie etwas aus einer Liste basierend auf einem zufälligen Double x auswählen müssen, das immer 0 <= x && x < 1 ist, ist es einfach.

return list[(int)(x * list.Count)];

Viel Spaß!

6voto

Xelights Punkte 119

Wenn Sie nichts dagegen haben, zwei Listen zu verwenden, ist dies wahrscheinlich der einfachste Weg, es zu tun, aber wahrscheinlich nicht der effizienteste oder vorhersehbarste:

List xList = new List() { 1, 2, 3, 4, 5 };
List deck = new List();

foreach (int xInt in xList)
    deck.Insert(random.Next(0, deck.Count + 1), xInt);

0 Stimmen

Sorgt dies nicht dafür, dass das erste Element gleich dem Original sein wird? Außerdem können Sie nicht an Index IList.Count einfügen, da dies zu ArgumentOutOfRangeException führen wird (learn.microsoft.com/ru-ru/dotnet/api/…)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X