Randomisieren Sie eine Liste <T>

854

Was ist der beste Weg, um die Reihenfolge einer generischen Liste in C # zufällig zu bestimmen? Ich habe einen endlichen Satz von 75 Zahlen in einer Liste, der ich eine zufällige Reihenfolge zuweisen möchte, um sie für eine Lotterieanwendung zu zeichnen.

mirezus
quelle
3
Es gibt ein offenes Problem bei der Integration dieser Funktionalität in .NET: github.com/dotnet/corefx/issues/461
Natan
5
Sie könnten an diesem NuGet-Paket interessiert sein , das Erweiterungsmethoden zum Mischen von IList <T> und IEnumerable <T> unter Verwendung des unten genannten Fisher-Yates-Algorithmus enthält
ChaseMedallion
3
@Natan haben sie das Problem geschlossen, weil jemand "an vielen Projekten gearbeitet und viele Bibliotheken entwickelt hat und nie eine solche Methode gebraucht hat", was mich verärgert hat. Jetzt müssen wir uns selbst untersuchen, nach den besten Implementierungen suchen und Zeit verschwenden, um das Rad einfach neu zu erfinden.
Vitalii Isaenko
1
Sehe ich das richtig? Keine einzige gültige funktionale Antwort nach 10 Jahren? Vielleicht brauchen wir eine weitere Prämie für eine Lösung, die die benötigte Entropie berücksichtigt, um eine Liste mit 75 Zahlen zu mischen $ log2 (75!) = 364 $ und wie wir diese erhalten können. Man müsste sogar ein kryptografisch sicheres RNG mit 256 Entropiebits mindestens einmal während eines Fischerei-Yates-Shuffle neu aussäen.
Falco
1
Und wenn der übliche Programmierer dieses Problem nicht lösen kann, haben wir alle für immer die gleichen 0,01% der möglichen Solitairespiele gespielt?
Falco

Antworten:

1136

Mische alle (I)Listmit einer Erweiterungsmethode, die auf dem Fisher-Yates-Shuffle basiert :

private static Random rng = new Random();  

public static void Shuffle<T>(this IList<T> list)  
{  
    int n = list.Count;  
    while (n > 1) {  
        n--;  
        int k = rng.Next(n + 1);  
        T value = list[k];  
        list[k] = list[n];  
        list[n] = value;  
    }  
}

Verwendungszweck:

List<Product> products = GetProducts();
products.Shuffle();

Der obige Code verwendet die viel kritisierte System.Random-Methode, um Swap-Kandidaten auszuwählen. Es ist schnell, aber nicht so zufällig, wie es sein sollte. Wenn Sie eine bessere Qualität der Zufälligkeit in Ihren Mischvorgängen benötigen, verwenden Sie den Zufallszahlengenerator in System.Security.Cryptography wie folgt:

using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
    RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
    int n = list.Count;
    while (n > 1)
    {
        byte[] box = new byte[1];
        do provider.GetBytes(box);
        while (!(box[0] < n * (Byte.MaxValue / n)));
        int k = (box[0] % n);
        n--;
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
    }
}

Ein einfacher Vergleich ist in diesem Blog (WayBack Machine) verfügbar .

Bearbeiten: Seit ich diese Antwort vor ein paar Jahren geschrieben habe, haben viele Leute mich kommentiert oder geschrieben, um auf den großen dummen Fehler in meinem Vergleich hinzuweisen. Sie haben natürlich recht. Es ist nichts falsch mit System.Random, wenn es so verwendet wird, wie es beabsichtigt war. In meinem ersten Beispiel oben instanziiere ich die Variable rng innerhalb der Shuffle-Methode, die nach Problemen fragt, wenn die Methode wiederholt aufgerufen werden soll. Unten finden Sie ein festes, vollständiges Beispiel, das auf einem wirklich nützlichen Kommentar basiert, den @weston heute hier auf SO erhalten hat.

Program.cs:

using System;
using System.Collections.Generic;
using System.Threading;

namespace SimpleLottery
{
  class Program
  {
    private static void Main(string[] args)
    {
      var numbers = new List<int>(Enumerable.Range(1, 75));
      numbers.Shuffle();
      Console.WriteLine("The winning numbers are: {0}", string.Join(",  ", numbers.GetRange(0, 5)));
    }
  }

  public static class ThreadSafeRandom
  {
      [ThreadStatic] private static Random Local;

      public static Random ThisThreadsRandom
      {
          get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
      }
  }

  static class MyExtensions
  {
    public static void Shuffle<T>(this IList<T> list)
    {
      int n = list.Count;
      while (n > 1)
      {
        n--;
        int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
      }
    }
  }
}
Granate
quelle
32
Was ist, wenn list.Count> Byte.MaxValue ist? Wenn n = 1000, dann ist 255/1000 = 0, so dass die do-Schleife eine Endlosschleife ist, da Box [0] <0 immer falsch ist.
AndrewS
18
Ich möchte darauf hinweisen, dass der Vergleich fehlerhaft ist. Die Verwendung von <code> new Random () </ code> in einer Schleife ist das Problem, nicht die Zufälligkeit von <code> Random </ code> Erläuterung
Sven
9
Es ist eine gute Idee, eine Instanz von Random an die Shuffle-Methode zu übergeben, anstatt sie im Inneren so zu erstellen, als würden Sie Shuffle viele Male schnell hintereinander aufrufen (z. B. viele kurze Listen mischen). Die Listen werden alle in derselben gemischt Weg (zB wird der erste Gegenstand immer auf Position 3 gebracht).
Mark Heath
7
Nur Random rng = new Random();ein zu machen staticwürde das Problem im Vergleichspost lösen. Da jeder nachfolgende Anruf dem letzten zufälligen Ergebnis des vorherigen Anrufs folgen würde.
Weston
5
# 2, es ist nicht klar, dass die Version mit dem Crypto-Generator funktioniert, da der maximale Bereich eines Bytes 255 beträgt, sodass eine größere Liste nicht korrekt gemischt wird.
Mark Sowul
336

Wenn wir nur Artikel in einer völlig zufälligen Reihenfolge mischen müssen (nur um die Artikel in einer Liste zu mischen), bevorzuge ich diesen einfachen, aber effektiven Code, der Artikel nach Guid ...

var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList();
user453230
quelle
40
GUIDs sollen eindeutig und nicht zufällig sein. Ein Teil davon ist maschinenbasiert und ein anderer Teil zeitbasiert, und nur ein kleiner Teil ist zufällig. blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx
Despertar
99
Dies ist eine schöne, elegante Lösung. Wenn Sie möchten, dass etwas anderes als eine Anleitung Zufälligkeit erzeugt, bestellen Sie einfach nach etwas anderem. Beispiel: var shuffledcards = cards.OrderBy(a => rng.Next()); compilr.com/grenade/sandbox/Program.cs
Granate
20
Bitte nicht. Das ist falsch. "Bestellung nach dem Zufallsprinzip" ist absolut kein Shuffle: Sie führen eine Voreingenommenheit ein und, schlimmer noch, Sie riskieren, in Endlosschleifen zu gehen
Vito De Tullio
78
@VitoDeTullio: Du erinnerst dich falsch. Sie riskieren Endlosschleifen, wenn Sie eine zufällige Vergleichsfunktion bereitstellen . Eine Vergleichsfunktion ist erforderlich, um eine konsistente Gesamtbestellung zu erstellen . Ein zufälliger Schlüssel ist in Ordnung. Dieser Vorschlag ist falsch, da nicht garantiert wird, dass Guids zufällig sind , und nicht, weil die Technik des Sortierens nach einem zufälligen Schlüssel falsch ist.
Eric Lippert
24
@Doug: NewGuidgarantiert nur, dass Sie eine eindeutige GUID erhalten. Es gibt keine Garantie für Zufälligkeit. Wenn Sie eine GUID für einen anderen Zweck als die Erstellung eines eindeutigen Werts verwenden, machen Sie es falsch.
Eric Lippert
120

Ich bin etwas überrascht von all den klobigen Versionen dieses einfachen Algorithmus hier. Fisher-Yates (oder Knuth Shuffle) ist etwas knifflig, aber sehr kompakt. Warum ist es schwierig? Weil Sie darauf achten müssen, ob Ihr Zufallszahlengenerator einen r(a,b)Wert zurückgibt, der binklusive oder exklusiv ist. Ich habe auch die Wikipedia-Beschreibung bearbeitet, damit die Leute dort nicht blindlings dem Pseudocode folgen und schwer zu erkennende Fehler erstellen. Gibt für .Net ohne weiteres eine Random.Next(a,b)exklusive Nummer bzurück. So kann es in C # /. Net implementiert werden:

public static void Shuffle<T>(this IList<T> list, Random rnd)
{
    for(var i=list.Count; i > 0; i--)
        list.Swap(0, rnd.Next(0, i));
}

public static void Swap<T>(this IList<T> list, int i, int j)
{
    var temp = list[i];
    list[i] = list[j];
    list[j] = temp;
}

Versuchen Sie diesen Code .

Shital Shah
quelle
Wäre es nicht besser, rnd (i, list.Count) in rnd (0, list.Count) zu ändern, damit jede Karte getauscht werden kann?
Donuts
16
@ Donuts - nein. Wenn Sie dies tun, werden Sie beim Mischen eine Voreingenommenheit hinzufügen.
Shital Shah
2
Wenn Sie Swap <T> in eine separate Methode aufteilen, verursachen Sie anscheinend viele unnötige T-Zuweisungen für Temp.
Clay
2
Ich würde argumentieren, dass LINQ möglicherweise die Leistung des Mischens verlangsamen könnte, und das wäre ein Grund, es nicht zu verwenden, insbesondere angesichts der relativen Einfachheit des Codes.
Winglerw28
7
Wann i = list.Count - 1, dh die letzte Iteration, rnd.Next(i, list.Count)gibt Ihnen i zurück. Sie benötigen daher i < list.Count -1als Schleifenbedingung. Nun, du brauchst es nicht, aber es spart 1 Iteration;)
Pod
78

Erweiterungsmethode für IEnumerable:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
    Random rnd = new Random();
    return source.OrderBy<T, int>((item) => rnd.Next());
}
Denis
quelle
3
Beachten Sie, dass dies nicht threadsicher ist, selbst wenn es auf einer thread-sicheren Liste verwendet wird
BlueRaja - Danny Pflughoeft
1
Wie geben wir dieser Funktion die Liste <string>?
MonsterMMORPG
8
Bei diesem Algorithmus gibt es zwei wesentliche Probleme: - OrderBySortiert die Elemente mithilfe einer QuickSort-Variante nach ihren (scheinbar zufälligen) Schlüsseln. Die QuickSort-Leistung beträgt O (N log N) . Im Gegensatz dazu ist ein Fisher-Yates-Shuffle O (N) . Bei einer Sammlung von 75 Elementen ist dies möglicherweise keine große Sache, aber der Unterschied wird bei größeren Sammlungen deutlich.
John Beyer
10
... - Random.Next()kann eine einigermaßen pseudozufällige Verteilung von Werten erzeugen, garantiert jedoch nicht , dass die Werte eindeutig sind. Die Wahrscheinlichkeit doppelter Schlüssel wächst (nicht linear) mit N, bis sie die Gewissheit erreicht, wenn N 2 ^ 32 + 1 erreicht. Der OrderByQuickSort ist eine stabile Sorte. Wenn also mehreren Elementen zufällig derselbe Pseudozufallsindexwert zugewiesen wird, ist ihre Reihenfolge in der Ausgabesequenz dieselbe wie in der Eingabesequenz. somit wird eine Vorspannung in das "Mischen" eingeführt.
John Beyer
27
@ JohnBeyer: Es gibt weitaus größere Probleme als diese Quelle der Voreingenommenheit. Random hat nur vier Milliarden mögliche Samen, was weitaus weniger ist als die Anzahl der möglichen Mischvorgänge eines mittelgroßen Sets. Nur ein winziger Bruchteil der möglichen Mischvorgänge kann generiert werden. Diese Vorspannung stellt die Vorspannung aufgrund versehentlicher Kollisionen in den Schatten.
Eric Lippert
16

Die Idee ist, ein anonymes Objekt mit Artikel und zufälliger Reihenfolge zu erhalten und die Artikel dann nach dieser Reihenfolge und dem Rückgabewert neu zu ordnen:

var result = items.Select(x => new { value = x, order = rnd.Next() })
            .OrderBy(x => x.order).Select(x => x.value).ToList()
Andrey Kucher
quelle
3
beste
Einzeiler-
1
Sie vermissen ein Semikolon am Ende fyi
reggaeguitar
Wenn jemand sich über rnd nicht sicher ist, fügen Sie dies vor dem obigen Code hinzu. Random rnd = new Random ();
Greg Trevellick
10
    public static List<T> Randomize<T>(List<T> list)
    {
        List<T> randomizedList = new List<T>();
        Random rnd = new Random();
        while (list.Count > 0)
        {
            int index = rnd.Next(0, list.Count); //pick a random item from the master list
            randomizedList.Add(list[index]); //place it at the end of the randomized list
            list.RemoveAt(index);
        }
        return randomizedList;
    }
Adam Tegen
quelle
4
Sollten Sie nicht so etwas wie var listCopy = list.ToList()vermeiden, dass alle Elemente von der eingehenden Liste gestrichen werden? Ich verstehe nicht wirklich, warum Sie diese Listen zum Leeren mutieren möchten.
Chris Marisic
9

BEARBEITEN Das RemoveAtist eine Schwäche in meiner vorherigen Version. Diese Lösung überwindet das.

public static IEnumerable<T> Shuffle<T>(
        this IEnumerable<T> 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 die Option Random generator: Wenn die Basis-Framework-Implementierung von Randomnicht threadsicher 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 RandomImplementierung finden Sie in dieser Antwort.


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

public static IEnumerable<T> Shuffle<T>(this IList<T> 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]];
}

Jodrell
quelle
Siehe stackoverflow.com/questions/4412405/… . Sie müssen sich bereits bewusst sein.
Nawfal
@nawfal siehe meine verbesserte Implementierung.
Jodrell
1
hmm fair genug. Ist es GetNextoder Next?
Nawfal
4

Sie können dies mit dieser einfachen Erweiterungsmethode erreichen

public static class IEnumerableExtensions
{

    public static IEnumerable<t> Randomize<t>(this IEnumerable<t> target)
    {
        Random r = new Random();

        return target.OrderBy(x=>(r.Next()));
    }        
}

und Sie können es verwenden, indem Sie die folgenden Schritte ausführen

// use this on any collection that implements IEnumerable!
// List, Array, HashSet, Collection, etc

List<string> myList = new List<string> { "hello", "random", "world", "foo", "bar", "bat", "baz" };

foreach (string s in myList.Randomize())
{
    Console.WriteLine(s);
}
Shehab Fawzy
quelle
3
Ich würde die RandomKlasseninstanz außerhalb der Funktion als staticVariable halten. Andernfalls erhalten Sie möglicherweise denselben Randomisierungs-Seed vom Timer, wenn Sie schnell hintereinander aufgerufen werden.
Lemonseed
Ein interessanter Hinweis: Wenn Sie die Zufallsklasse innerhalb einer Schleife schnell instanziieren, z. B. zwischen 0 ms und 200 ms voneinander, haben Sie eine sehr hohe Wahrscheinlichkeit, dass Sie denselben Randomisierungs-Seed erhalten - was dann zu wiederholten Ergebnissen führt. Sie können dies jedoch umgehen, indem Sie Random rand = new Random (Guid.NewGuid (). GetHashCode ()) verwenden. Dies erzwingt effektiv die Ableitung der Randomisierung von Guid.NewGuid ()
Baaleos
4

Dies ist meine bevorzugte Methode für ein Mischen, wenn es wünschenswert ist, das Original nicht zu ändern. Es ist eine Variante des Fisher-Yates-Algorithmus "Inside-Out" , der mit jeder aufzählbaren Sequenz funktioniert (die Länge von sourcemuss nicht von Anfang an bekannt sein).

public static IList<T> NextList<T>(this Random r, IEnumerable<T> source)
{
  var list = new List<T>();
  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 implementiert werden, indem ein Bereich von 0bis length - 1zugewiesen und die Indizes zufällig erschöpft werden, indem der zufällig ausgewählte Index gegen den letzten Index ausgetauscht wird, bis alle Indizes genau einmal ausgewählt wurden. Dieser obige Code führt genau das Gleiche aus, jedoch ohne die zusätzliche Zuordnung. Welches ist ziemlich ordentlich.

In Bezug auf die RandomKlasse handelt es sich um einen Allzweck-Zahlengenerator (und wenn ich eine Lotterie betreiben würde, würde ich in Betracht ziehen, etwas anderes zu verwenden). Standardmäßig wird auch ein zeitbasierter Startwert verwendet. Eine kleine Lösung des Problems besteht darin, die RandomKlasse mit dem zu setzen, RNGCryptoServiceProvideroder Sie könnten das RNGCryptoServiceProviderin einer ähnlichen Methode (siehe unten) verwenden, um einheitlich ausgewählte zufällige doppelte Gleitkommawerte zu generieren, aber um eine Lotterie durchzuführen, müssen Sie die Zufälligkeit und die Art von verstehen die Zufallsquelle.

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

Der Punkt des Erzeugens eines zufälligen Doppels (ausschließlich zwischen 0 und 1) besteht darin, auf eine ganzzahlige Lösung zu skalieren. Wenn Sie etwas aus einer Liste auswählen müssen, das auf einem zufälligen Double basiert x, ist dies immer 0 <= x && x < 1einfach.

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

Genießen!

John Leidegren
quelle
4

Wenn es Ihnen nichts ausmacht, zwei zu verwenden Lists, ist dies wahrscheinlich der einfachste Weg, aber wahrscheinlich nicht der effizienteste oder unvorhersehbarste:

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

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

Wenn Sie eine feste Zahl (75) haben, können Sie ein Array mit 75 Elementen erstellen, dann Ihre Liste auflisten und die Elemente an zufällige Positionen im Array verschieben. Sie können die Zuordnung der Listennummer zum Array-Index mithilfe der Fisher-Yates-Zufallswiedergabe generieren .

dmo
quelle
3

Ich benutze normalerweise:

var list = new List<T> ();
fillList (list);
var randomizedList = new List<T> ();
var rnd = new Random ();
while (list.Count != 0)
{
    var index = rnd.Next (0, list.Count);
    randomizedList.Add (list [index]);
    list.RemoveAt (index);
}
Albertein
quelle
list.RemoveAt ist eine O (n) -Operation, die diese Implementierung unerschwinglich langsam macht.
George Polevoy
1
    List<T> OriginalList = new List<T>();
    List<T> TempList = new List<T>();
    Random random = new Random();
    int length = OriginalList.Count;
    int TempIndex = 0;

    while (length > 0) {
        TempIndex = random.Next(0, length);  // get random value between 0 and original length
        TempList.Add(OriginalList[TempIndex]); // add to temp list
        OriginalList.RemoveAt(TempIndex); // remove from original list
        length = OriginalList.Count;  // get new list <T> length.
    }

    OriginalList = new List<T>();
    OriginalList = TempList; // copy all items from temp list to original list.
user7767814
quelle
0

Hier ist ein effizienter Shuffler, der ein Byte-Array von Shuffled-Werten zurückgibt. Es mischt nie mehr als nötig. Es kann an der Stelle neu gestartet werden, an der es zuvor aufgehört hat. Meine eigentliche Implementierung (nicht gezeigt) ist eine MEF-Komponente, die einen benutzerdefinierten Ersatzmischer ermöglicht.

    public byte[] Shuffle(byte[] array, int start, int count)
    {
        int n = array.Length - start;
        byte[] shuffled = new byte[count];
        for(int i = 0; i < count; i++, start++)
        {
            int k = UniformRandomGenerator.Next(n--) + start;
            shuffled[i] = array[k];
            array[k] = array[start];
            array[start] = shuffled[i];
        }
        return shuffled;
    }

`

BSalita
quelle
0

Hier ist eine thread-sichere Möglichkeit, dies zu tun:

public static class EnumerableExtension
{
    private static Random globalRng = new Random();

    [ThreadStatic]
    private static Random _rng;

    private static Random rng 
    {
        get
        {
            if (_rng == null)
            {
                int seed;
                lock (globalRng)
                {
                    seed = globalRng.Next();
                }
                _rng = new Random(seed);
             }
             return _rng;
         }
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
    {
        return items.OrderBy (i => rng.Next());
    }
}
Christopher Stevenson
quelle
0
 public Deck(IEnumerable<Card> initialCards) 
    {
    cards = new List<Card>(initialCards);
    public void Shuffle() 
     }
    {
        List<Card> NewCards = new List<Card>();
        while (cards.Count > 0) 
        {
            int CardToMove = random.Next(cards.Count);
            NewCards.Add(cards[CardToMove]);
            cards.RemoveAt(CardToMove);
        }
        cards = NewCards;
    }

public IEnumerable<string> GetCardNames() 

{
    string[] CardNames = new string[cards.Count];
    for (int i = 0; i < cards.Count; i++)
    CardNames[i] = cards[i].Name;
    return CardNames;
}

Deck deck1;
Deck deck2;
Random random = new Random();

public Form1() 
{

InitializeComponent();
ResetDeck(1);
ResetDeck(2);
RedrawDeck(1);
 RedrawDeck(2);

}



 private void ResetDeck(int deckNumber) 
    {
    if (deckNumber == 1) 
{
      int numberOfCards = random.Next(1, 11);
      deck1 = new Deck(new Card[] { });
      for (int i = 0; i < numberOfCards; i++)
           deck1.Add(new Card((Suits)random.Next(4),(Values)random.Next(1, 14)));
       deck1.Sort();
}


   else
    deck2 = new Deck();
 }

private void reset1_Click(object sender, EventArgs e) {
ResetDeck(1);
RedrawDeck(1);

}

private void shuffle1_Click(object sender, EventArgs e) 
{
    deck1.Shuffle();
    RedrawDeck(1);

}

private void moveToDeck1_Click(object sender, EventArgs e) 
{

    if (listBox2.SelectedIndex >= 0)
    if (deck2.Count > 0) {
    deck1.Add(deck2.Deal(listBox2.SelectedIndex));

}

    RedrawDeck(1);
    RedrawDeck(2);

}
sumit laddha
quelle
2
Willkommen bei Stack Overflow! Bitte erwägen Sie, Ihrer Antwort eine Erklärung hinzuzufügen, anstatt nur einen riesigen Codeblock. Unser Ziel ist es, die Menschen so zu erziehen, dass sie die Antwort verstehen und in anderen Situationen anwenden können. Wenn Sie Ihren Code kommentieren und eine Erklärung hinzufügen, wird Ihre Antwort nicht nur für die Person hilfreicher, die diesmal die Frage gestellt hat, sondern auch für alle Personen, die in Zukunft möglicherweise das gleiche Problem haben.
Starsplusplus
4
Der größte Teil dieses Codes ist für die Frage völlig irrelevant, und der einzig nützliche Teil wiederholt im Grunde die Antwort von Adam Tegen von vor fast 6 Jahren.
TC
0

Eine einfache Änderung der akzeptierten Antwort , die eine neue Liste zurückgibt, anstatt direkt zu arbeiten, und die allgemeinere akzeptiert, IEnumerable<T>wie dies bei vielen anderen Linq-Methoden der Fall ist.

private static Random rng = new Random();

/// <summary>
/// Returns a new list where the elements are randomly shuffled.
/// Based on the Fisher-Yates shuffle, which has O(n) complexity.
/// </summary>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list) {
    var source = list.ToList();
    int n = source.Count;
    var shuffled = new List<T>(n);
    shuffled.AddRange(source);
    while (n > 1) {
        n--;
        int k = rng.Next(n + 1);
        T value = shuffled[k];
        shuffled[k] = shuffled[n];
        shuffled[n] = value;
    }
    return shuffled;
}
Extragorey
quelle
-5

Sicher alter Beitrag, aber ich benutze nur eine GUID.

Items = Items.OrderBy(o => Guid.NewGuid().ToString()).ToList();

Eine GUID ist immer eindeutig, und da sie jedes Mal neu generiert wird, ändert sich das Ergebnis jedes Mal.

DavidMc
quelle
Kompakt, aber haben Sie einen Hinweis auf die Sortierung aufeinanderfolgender newGuids, um zufällig von hoher Qualität zu sein? Einige Versionen von quid / uuid haben Zeitstempel und andere nicht zufällige Teile.
Johan Lundberg
8
Diese Antwort wurde bereits gegeben, und schlimmer noch, sie ist auf Einzigartigkeit und nicht auf Zufälligkeit ausgelegt.
Alex Angas
-7

Ein sehr einfacher Ansatz für diese Art von Problem besteht darin, eine Reihe von zufälligen Elementtauschvorgängen in der Liste zu verwenden.

Im Pseudocode würde dies so aussehen:

do 
    r1 = randomPositionInList()
    r2 = randomPositionInList()
    swap elements at index r1 and index r2 
for a certain number of times
Aleris
quelle
1
Ein Problem bei diesem Ansatz besteht darin, zu wissen, wann aufzuhören ist. Es hat auch die Tendenz, Verzerrungen im Pseudozufallszahlengenerator zu übertreiben.
Mark Bessey
3
Ja. Sehr ineffizient. Es gibt keinen Grund, einen solchen Ansatz zu verwenden, wenn bessere, schnellere Ansätze existieren, die genauso einfach sind.
PeterAllenWebb
1
nicht sehr effizient oder effektiv ... N-maliges Ausführen würde wahrscheinlich viele Elemente in ihrer ursprünglichen Position belassen.
NSjonas