Ist die Verwendung von Random und OrderBy ein guter Shuffle-Algorithmus?

164

Ich habe einen Artikel über verschiedene Shuffle-Algorithmen bei Coding Horror gelesen . Ich habe gesehen, dass irgendwo Leute dies getan haben, um eine Liste zu mischen:

var r = new Random();
var shuffled = ordered.OrderBy(x => r.Next());

Ist das ein guter Shuffle-Algorithmus? Wie funktioniert es genau? Ist das ein akzeptabler Weg?

Svish
quelle

Antworten:

205

Es ist keine Art zu mischen, die ich mag, hauptsächlich mit der Begründung, dass es O (n log n) ist, ohne guten Grund, wenn es einfach ist, ein O (n) Shuffle zu implementieren. Der Code in der Frage "funktioniert", indem er jedem Element eine zufällige (hoffentlich eindeutige!) Nummer gibt und die Elemente dann nach dieser Nummer ordnet.

Ich bevorzuge Durstenfields Variante des Fisher-Yates-Shuffle, bei dem Elemente ausgetauscht werden.

Die Implementierung einer einfachen ShuffleErweiterungsmethode würde im Wesentlichen darin bestehen, die Eingabe aufzurufen ToListoder ToArrayzu verwenden und dann eine vorhandene Implementierung von Fisher-Yates zu verwenden. (Geben Sie den Randomals Parameter ein, um das Leben im Allgemeinen schöner zu machen.) Es gibt viele Implementierungen ... Ich habe wahrscheinlich irgendwo eine Antwort.

Das Schöne an einer solchen Erweiterungsmethode ist, dass dem Leser dann sehr klar ist, was Sie tatsächlich versuchen.

EDIT: Hier ist eine einfache Implementierung (keine Fehlerprüfung!):

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length-1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        T tmp = elements[i];
        elements[i] = elements[swapIndex];
        elements[swapIndex] = tmp;
    }
    // Lazily yield (avoiding aliasing issues etc)
    foreach (T element in elements)
    {
        yield return element;
    }
}

BEARBEITEN: Die folgenden Kommentare zur Leistung haben mich daran erinnert, dass wir die Elemente tatsächlich zurückgeben können, wenn wir sie mischen:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    for (int i = elements.Length - 1; i >= 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        // ... except we don't really need to swap it fully, as we can
        // return it immediately, and afterwards it's irrelevant.
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

Dies erledigt jetzt nur noch so viel Arbeit wie nötig.

Beachten Sie, dass Sie in beiden Fällen vorsichtig mit der von RandomIhnen verwendeten Instanz sein müssen :

  • Wenn Sie zwei Instanzen von Randomungefähr zur gleichen Zeit erstellen, erhalten Sie dieselbe Folge von Zufallszahlen (wenn Sie auf dieselbe Weise verwendet werden).
  • Random ist nicht threadsicher.

Ich habe einen Artikel,Random der ausführlicher auf diese Probleme eingeht und Lösungen bietet.

Jon Skeet
quelle
5
Nun, Implementierungen für kleine, aber wichtige Dinge wie diese, würde ich sagen, sind hier auf StackOverflow immer schön zu finden. Also ja bitte, wenn du willst =)
Svish
9
Jon - Ihre Erklärung von Fisher-Yates entspricht der in der Frage angegebenen Implementierung (die naive Version). Durstenfeld / Knuth erreichen O (n) nicht durch Zuordnung, sondern durch Auswahl aus einer abnehmenden Menge und Austausch. Auf diese Weise kann sich die ausgewählte Zufallszahl wiederholen und der Algorithmus nimmt nur O (n).
Tvanfosson
8
Sie haben es wahrscheinlich satt, von mir zu hören, aber ich bin bei meinen Unit-Tests auf ein kleines Problem gestoßen, dessen Sie sich vielleicht bewusst sein möchten. ElementAt weist eine Besonderheit auf, die dazu führt, dass die Erweiterung jedes Mal aufgerufen wird und unzuverlässige Ergebnisse erzielt werden. In meinen Tests materialisiere ich das Ergebnis, bevor ich es überprüfe, um dies zu vermeiden.
Tvanfosson
3
@tvanfosson: Überhaupt nicht krank :) Aber ja, Anrufer sollten sich bewusst sein, dass es träge ausgewertet wird.
Jon Skeet
4
Ein bisschen spät, aber bitte beachten source.ToArray();Sie, dass Sie using System.Linq;in der gleichen Datei haben müssen. Wenn Sie dies nicht tun, erhalten Sie folgende Fehlermeldung:'System.Collections.Generic.IEnumerable<T>' does not contain a definition for 'ToArray' and no extension method 'ToArray' accepting a first argument of type 'System.Collections.Generic.IEnumerable<T>' could be found (are you missing a using directive or an assembly reference?)
Powerlord
70

Dies basiert auf Jon Skeets Antwort .

In dieser Antwort wird das Array gemischt und dann mit zurückgegeben yield. Das Nettoergebnis ist, dass das Array für die Dauer von foreach sowie für die Iteration erforderliche Objekte im Speicher gehalten wird und die Kosten dennoch am Anfang stehen - die Ausbeute ist im Grunde eine leere Schleife.

Dieser Algorithmus wird häufig in Spielen verwendet, in denen die ersten drei Elemente ausgewählt werden und die anderen erst später, wenn überhaupt, benötigt werden. Mein Vorschlag ist, yielddie Nummern zu tauschen, sobald sie getauscht werden. Dies reduziert die Startkosten, während die Iterationskosten bei O (1) bleiben (im Grunde 5 Operationen pro Iteration). Die Gesamtkosten würden gleich bleiben, aber das Mischen selbst würde schneller sein. In Fällen, in denen dies aufgerufen collection.Shuffle().ToArray()wird, macht es theoretisch keinen Unterschied, aber in den oben genannten Anwendungsfällen beschleunigt es den Start. Dies würde den Algorithmus auch für Fälle nützlich machen, in denen Sie nur wenige eindeutige Elemente benötigen. Wenn Sie beispielsweise drei Karten aus einem 52er-Deck herausziehen müssen, können Sie anrufen deck.Shuffle().Take(3)und es finden nur drei Swaps statt (obwohl das gesamte Array zuerst kopiert werden müsste).

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length - 1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
        // we don't actually perform the swap, we can forget about the
        // swapped element because we already returned it.
    }

    // there is one item remaining that was not returned - we return it now
    yield return elements[0]; 
}
Konfigurator
quelle
Autsch! Dadurch werden wahrscheinlich nicht alle Elemente in der Quelle zurückgegeben. Sie können sich nicht darauf verlassen, dass eine Zufallszahl für N Iterationen eindeutig ist.
P Daddy
2
Klug! (Und ich hasse dieses Zeug mit 15 Charakteren ...)
Svish
@P Daddy: Huh? Möchtest du das näher erläutern?
Svish
1
Oder Sie könnten die> 0 durch> = 0 ersetzen und müssen nicht (obwohl ein zusätzlicher RNG-Treffer plus eine redundante Zuweisung)
FryGuy
4
Die Startkosten betragen O (N) als Kosten der Quelle. ToArray ();
Dave Hillier
8

Ausgehend von diesem Zitat von Skeet:

Es ist keine Art zu mischen, die ich mag, hauptsächlich mit der Begründung, dass es O (n log n) ist, ohne guten Grund, wenn es einfach ist, ein O (n) Mischen zu implementieren. Der Code in der Frage "funktioniert", indem er jedem Element eine zufällige ( hoffentlich eindeutige! ) Nummer gibt und die Elemente dann nach dieser Nummer ordnet.

Ich werde ein wenig den Grund für das hoffentlich Einzigartige erklären !

Nun aus dem Enumerable.OrderBy :

Diese Methode führt eine stabile Sortierung durch. Das heißt, wenn die Schlüssel zweier Elemente gleich sind, bleibt die Reihenfolge der Elemente erhalten

Dies ist sehr wichtig! Was passiert, wenn zwei Elemente dieselbe Zufallszahl "erhalten"? Es kommt vor, dass sie in derselben Reihenfolge bleiben, in der sie sich im Array befinden. Was ist nun die Möglichkeit dafür? Es ist schwierig, genau zu berechnen, aber es gibt das Geburtstagsproblem , das genau dieses Problem ist.

Ist es jetzt real? Ist es wahr?

Wie immer schreiben Sie im Zweifelsfall einige Programmzeilen: http://pastebin.com/5CDnUxPG

Dieser kleine Codeblock mischt ein Array von 3 Elementen eine bestimmte Anzahl von Malen, wobei der Fisher-Yates-Algorithmus rückwärts und der Fisher-Yates-Algorithmus vorwärts ausgeführt wird (auf der Wiki- Seite gibt es zwei Pseudocode-Algorithmen ... Sie erzeugen Äquivalente Ergebnisse, aber eines wird vom ersten bis zum letzten Element ausgeführt, während das andere vom letzten bis zum ersten Element ausgeführt wird), der naive falsche Algorithmus von http://blog.codinghorror.com/the-danger-of-naivete/ und unter Verwendung des .OrderBy(x => r.Next())und die .OrderBy(x => r.Next(someValue)).

Nun Random.Next ist

Eine 32-Bit-Ganzzahl mit Vorzeichen, die größer oder gleich 0 und kleiner als MaxValue ist.

also ist es gleichbedeutend mit

OrderBy(x => r.Next(int.MaxValue))

Um zu testen, ob dieses Problem besteht, können wir das Array vergrößern (etwas sehr Langsames) oder einfach den Maximalwert des Zufallszahlengenerators verringern ( int.MaxValuekeine "spezielle" Zahl ... Es ist einfach eine sehr große Zahl). Wenn der Algorithmus nicht durch die Stabilität von verzerrt ist, sollte letztendlich OrderByjeder Wertebereich das gleiche Ergebnis liefern.

Das Programm testet dann einige Werte im Bereich von 1 bis 4096. Wenn man das Ergebnis betrachtet, ist es ziemlich klar, dass der Algorithmus für niedrige Werte (<128) sehr voreingenommen ist (4-8%). Mit 3 Werten benötigen Sie mindestens r.Next(1024). Wenn Sie das Array größer machen (4 oder 5), r.Next(1024)reicht es nicht einmal aus. Ich bin kein Experte für Mischen und Mathematik, aber ich denke, dass Sie für jedes zusätzliche Bit der Länge des Arrays 2 zusätzliche Bits des Maximalwerts benötigen (da das Geburtstagsparadoxon mit dem sqrt (numvalues) verbunden ist) Wenn der Maximalwert 2 ^ 31 ist, sollten Sie in der Lage sein, Arrays mit bis zu 2 ^ 12/2 ^ 13 Bits (4096-8192 Elemente) zu sortieren.

Xanatos
quelle
Gut gesagt und zeigt perfekt ein Problem mit der ursprünglichen Frage. Dies sollte mit Jons Antwort zusammengeführt werden.
TheSoftwareJedi
6

Es ist wahrscheinlich für die meisten Zwecke in Ordnung und erzeugt fast immer eine wirklich zufällige Verteilung (außer wenn Random.Next () zwei identische zufällige ganze Zahlen erzeugt).

Es funktioniert, indem jedem Element der Reihe eine zufällige Ganzzahl zugewiesen wird und die Reihenfolge dann nach diesen Ganzzahlen geordnet wird.

Dies ist für 99,9% der Anwendungen völlig akzeptabel (es sei denn, Sie müssen den obigen Randfall unbedingt bearbeiten). Außerdem ist der Einwand von skeet gegen seine Laufzeit gültig. Wenn Sie also eine lange Liste mischen, möchten Sie sie möglicherweise nicht verwenden.

ripper234
quelle
4

Dies ist schon oft vorgekommen. Suche nach Fisher-Yates auf StackOverflow.

Hier ist ein C # -Codebeispiel, das ich für diesen Algorithmus geschrieben habe. Sie können es bei Bedarf auch für einen anderen Typ parametrisieren.

static public class FisherYates
{
        //      Based on Java code from wikipedia:
        //      http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
        static public void Shuffle(int[] deck)
        {
                Random r = new Random();
                for (int n = deck.Length - 1; n > 0; --n)
                {
                        int k = r.Next(n+1);
                        int temp = deck[n];
                        deck[n] = deck[k];
                        deck[k] = temp;
                }
        }
}
hughdbrown
quelle
2
Sie sollten Randomdiese statische Variable nicht verwenden - sie Randomist nicht threadsicher. Siehe csharpindepth.com/Articles/Chapter12/Random.aspx
Jon Skeet
@ Jon Skeet: Sicher, das ist ein legitimes Argument. OTOH, das OP fragte nach einem Algorithmus, der völlig falsch war, obwohl dies korrekt ist (abgesehen vom Multithread-Anwendungsfall für das Mischen von Karten).
Hughdbrown
1
Das bedeutet nur, dass dies "weniger falsch" ist als der Ansatz des OP. Dies bedeutet nicht, dass Code verwendet werden sollte, ohne zu verstehen, dass er in einem Multithread-Kontext nicht sicher verwendet werden kann. Dies haben Sie nicht erwähnt. Es besteht die begründete Erwartung, dass statische Elemente sicher von mehreren Threads aus verwendet werden können.
Jon Skeet
@ Jon Skeet: Klar, ich kann es ändern. Getan. Ich neige dazu zu denken, dass es übertrieben ist, auf eine vor dreieinhalb Jahren beantwortete Frage zurückzukommen und zu sagen: "Es ist falsch, weil es den Multithread-Anwendungsfall nicht behandelt", wenn das OP nie nach mehr als dem Algorithmus gefragt hat. Überprüfen Sie meine Antworten im Laufe der Jahre. Oft habe ich OP-Antworten gegeben, die über die angegebenen Anforderungen hinausgingen. Ich wurde dafür kritisiert. Ich würde jedoch nicht erwarten, dass OPs Antworten erhalten, die für alle Verwendungszwecke geeignet sind.
Hughdbrown
Ich habe diese Antwort überhaupt nur besucht, weil mich jemand anderes im Chat darauf hingewiesen hat. Obwohl das OP Threading nicht ausdrücklich erwähnt hat, ist es meiner Meinung nach definitiv erwähnenswert, wenn eine statische Methode nicht threadsicher ist, da dies ungewöhnlich ist und den Code für viele Situationen ohne Änderung ungeeignet macht. Ihr neuer Code ist threadsicher - aber immer noch nicht ideal, als würden Sie ihn "ungefähr" gleichzeitig aus mehreren Threads aufrufen, um zwei Sammlungen derselben Größe zu mischen. Die Shuffles sind gleichwertig. Grundsätzlich Randomist ein Schmerz zu verwenden, wie in meinem Artikel erwähnt.
Jon Skeet
3

Scheint ein guter Mischalgorithmus zu sein, wenn Sie sich keine Sorgen um die Leistung machen. Das einzige Problem, auf das ich hinweisen möchte, ist, dass das Verhalten nicht steuerbar ist, sodass Sie möglicherweise Schwierigkeiten haben, es zu testen.

Eine mögliche Option besteht darin, einen Startwert als Parameter an den Zufallszahlengenerator (oder den Zufallsgenerator als Parameter) zu übergeben, damit Sie mehr Kontrolle haben und ihn einfacher testen können.

Samuel Carrijo
quelle
3

Ich fand Jon Skeets Antwort völlig zufriedenstellend, aber der Robo-Scanner meines Kunden meldet jeden Fall Randomals Sicherheitslücke. Also habe ich es ausgetauscht System.Security.Cryptography.RNGCryptoServiceProvider. Als Bonus wird das erwähnte Thread-Sicherheitsproblem behoben. Auf der anderen Seite wurde RNGCryptoServiceProviderals 300x langsamer als mit gemessen gemessen Random.

Verwendung:

using (var rng = new RNGCryptoServiceProvider())
{
    var data = new byte[4];
    yourCollection = yourCollection.Shuffle(rng, data);
}

Methode:

/// <summary>
/// Shuffles the elements of a sequence randomly.
/// </summary>
/// <param name="source">A sequence of values to shuffle.</param>
/// <param name="rng">An instance of a random number generator.</param>
/// <param name="data">A placeholder to generate random bytes into.</param>
/// <returns>A sequence whose elements are shuffled randomly.</returns>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, RNGCryptoServiceProvider rng, byte[] data)
{
    var elements = source.ToArray();
    for (int i = elements.Length - 1; i >= 0; i--)
    {
        rng.GetBytes(data);
        var swapIndex = BitConverter.ToUInt32(data, 0) % (i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}
Frattaro
quelle
3

Suchen Sie einen Algorithmus? Du kannst meine ShuffleListKlasse benutzen :

class ShuffleList<T> : List<T>
{
    public void Shuffle()
    {
        Random random = new Random();
        for (int count = Count; count > 0; count--)
        {
            int i = random.Next(count);
            Add(this[i]);
            RemoveAt(i);
        }
    }
}

Verwenden Sie es dann folgendermaßen:

ShuffleList<int> list = new ShuffleList<int>();
// Add elements to your list.
list.Shuffle();

Wie funktioniert es?

Nehmen wir eine erste sortierte Liste der 5 ersten ganzen Zahlen : { 0, 1, 2, 3, 4 }.

Die Methode beginnt mit dem Zählen der Anzahl der Elemente und ruft sie auf count. Dann, mit countauf jedem Schritt abnimmt, nimmt es eine Zufallszahl zwischen 0und countund bewegt ihn an das Ende der Liste.

Im folgenden schrittweisen Beispiel sind die Elemente, die verschoben werden könnten, kursiv und das ausgewählte Element fett gedruckt :

0 1 2 3 4
0 1 2 3 4
0 1 2 4 3
0 1 2 4 3
1 2 4 3 0
1 2 4 3 0
1 2 3 0 4
1 2 3 0 4
2 3 0 4 1
2 3 0 4 1
3 0 4 1 2

SteeveDroz
quelle
Das ist nicht O (n). RemoveAt alleine ist O (n).
Paparazzo
Hmm, es scheint, als hättest du recht, mein Schlechtes! Ich werde diesen Teil entfernen.
SteeveDroz
1

Dieser Algorithmus mischt, indem er für jeden Wert in einer Liste einen neuen Zufallswert generiert und die Liste dann nach diesen Zufallswerten sortiert. Stellen Sie sich vor, Sie fügen einer In-Memory-Tabelle eine neue Spalte hinzu, füllen sie dann mit GUIDs und sortieren nach dieser Spalte. Sieht für mich nach einem effizienten Weg aus (besonders mit dem Lambda-Zucker!)

Dave Swersky
quelle
1

Etwas unabhängig, aber hier ist eine interessante Methode (die, obwohl sie wirklich übertrieben ist, WIRKLICH implementiert wurde) für die wirklich zufällige Erzeugung von Würfeln!

Dice-O-Matic

Der Grund, warum ich dies hier poste, ist, dass er einige interessante Punkte darüber macht, wie seine Benutzer auf die Idee reagiert haben, Algorithmen zum Mischen über tatsächliche Würfel zu verwenden. In der realen Welt ist eine solche Lösung natürlich nur für die wirklich extremen Bereiche des Spektrums geeignet, in denen Zufälligkeit einen so großen Einfluss hat und möglicherweise den Einfluss auf das Geld hat;).

Irfy
quelle
1

Ich würde sagen, dass viele Antworten hier wie "Dieser Algorithmus mischt, indem er für jeden Wert in einer Liste einen neuen Zufallswert generiert und dann die Liste nach diesen Zufallswerten ordnet" möglicherweise sehr falsch sind!

Ich würde denken, dass dies NICHT jedem Element der Quellensammlung einen zufälligen Wert zuweist. Stattdessen könnte es einen Sortieralgorithmus geben, der wie Quicksort läuft und eine Vergleichsfunktion ungefähr n log n-mal aufruft. Einige Sorten erwarten wirklich, dass diese Vergleichsfunktion stabil ist und immer das gleiche Ergebnis liefert!

Könnte es nicht sein, dass der IEnumerableSorter eine Vergleichsfunktion für jeden Algorithmusschritt von z. B. Quicksort aufruft und jedes Mal die Funktion x => r.Next()für beide Parameter aufruft, ohne diese zwischenzuspeichern!

In diesem Fall könnten Sie den Sortieralgorithmus wirklich durcheinander bringen und ihn viel schlechter machen als die Erwartungen, auf denen der Algorithmus aufgebaut ist. Natürlich wird es irgendwann stabil und gibt etwas zurück.

Ich könnte es später überprüfen, indem ich die Debugging-Ausgabe in eine neue "Weiter" -Funktion setze, um zu sehen, was passiert. In Reflector konnte ich nicht sofort herausfinden, wie es funktioniert.

Christian
quelle
1
Dies ist nicht der Fall: Interne Überschreibung void ComputeKeys (TElement [] -Elemente, int count); Deklarationstyp: System.Linq.EnumerableSorter <TElement, TKey> Assembly: System.Core, Version = 3.5.0.0 Diese Funktion erstellt zuerst ein Array mit allen Schlüsseln, die Speicher verbrauchen, bevor sie von QuickSort sortiert werden
Christian
Das ist gut zu wissen - aber immer noch nur ein Implementierungsdetail, das sich in zukünftigen Versionen möglicherweise ändern könnte!
Blorgbeard ist
-5

Startzeit für die Ausführung von Code mit allen Threads löschen und jeden neuen Test zwischenspeichern,

Erster erfolgloser Code. Es läuft auf LINQPad. Wenn Sie folgen, um diesen Code zu testen.

Stopwatch st = new Stopwatch();
st.Start();
var r = new Random();
List<string[]> list = new List<string[]>();
list.Add(new String[] {"1","X"});
list.Add(new String[] {"2","A"});
list.Add(new String[] {"3","B"});
list.Add(new String[] {"4","C"});
list.Add(new String[] {"5","D"});
list.Add(new String[] {"6","E"});

//list.OrderBy (l => r.Next()).Dump();
list.OrderBy (l => Guid.NewGuid()).Dump();
st.Stop();
Console.WriteLine(st.Elapsed.TotalMilliseconds);

list.OrderBy (x => r.Next ()) verwendet 38,6528 ms

list.OrderBy (x => Guid.NewGuid ()) verwendet 36,7634 ms (wird von MSDN empfohlen.)

das nach dem zweiten Mal verwenden beide gleichzeitig.

BEARBEITEN: TESTCODE auf Intel Core i7 [email protected], Ram 8 GB DDR3 @ 1600, HDD SATA 5200 U / min mit [Daten: www.dropbox.com/s/pbtmh5s9lw285kp/data]

using System;
using System.Runtime;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Threading;

namespace Algorithm
{
    class Program
    {
        public static void Main(string[] args)
        {
            try {
                int i = 0;
                int limit = 10;
                var result = GetTestRandomSort(limit);
                foreach (var element in result) {
                    Console.WriteLine();
                    Console.WriteLine("time {0}: {1} ms", ++i, element);
                }
            } catch (Exception e) {
                Console.WriteLine(e.Message);
            } finally {
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
            }
        }

        public static IEnumerable<double> GetTestRandomSort(int limit)
        {
            for (int i = 0; i < 5; i++) {
                string path = null, temp = null;
                Stopwatch st = null;
                StreamReader sr = null;
                int? count = null;
                List<string> list = null;
                Random r = null;

                GC.Collect();
                GC.WaitForPendingFinalizers();
                Thread.Sleep(5000);

                st = Stopwatch.StartNew();
                #region Import Input Data
                path = Environment.CurrentDirectory + "\\data";
                list = new List<string>();
                sr = new StreamReader(path);
                count = 0;
                while (count < limit && (temp = sr.ReadLine()) != null) {
//                  Console.WriteLine(temp);
                    list.Add(temp);
                    count++;
                }
                sr.Close();
                #endregion

//              Console.WriteLine("--------------Random--------------");
//              #region Sort by Random with OrderBy(random.Next())
//              r = new Random();
//              list = list.OrderBy(l => r.Next()).ToList();
//              #endregion

//              #region Sort by Random with OrderBy(Guid)
//              list = list.OrderBy(l => Guid.NewGuid()).ToList();
//              #endregion

//              #region Sort by Random with Parallel and OrderBy(random.Next())
//              r = new Random();
//              list = list.AsParallel().OrderBy(l => r.Next()).ToList();
//              #endregion

//              #region Sort by Random with Parallel OrderBy(Guid)
//              list = list.AsParallel().OrderBy(l => Guid.NewGuid()).ToList();
//              #endregion

//              #region Sort by Random with User-Defined Shuffle Method
//              r = new Random();
//              list = list.Shuffle(r).ToList();
//              #endregion

//              #region Sort by Random with Parallel User-Defined Shuffle Method
//              r = new Random();
//              list = list.AsParallel().Shuffle(r).ToList();
//              #endregion

                // Result
//              
                st.Stop();
                yield return st.Elapsed.TotalMilliseconds;
                foreach (var element in list) {
                Console.WriteLine(element);
            }
            }

        }
    }
}

Ergebnisbeschreibung: https://www.dropbox.com/s/9dw9wl259dfs04g/ResultDescription.PNG
Ergebnisstatistik: https://www.dropbox.com/s/ewq5ybtsvesme4d/ResultStat.PNG

Schlussfolgerung:
Angenommen, LINQ OrderBy (r.Next ()) und OrderBy (Guid.NewGuid ()) sind in der ersten Lösung nicht schlechter als die benutzerdefinierte Zufallsmethode.

Antwort: Sie sind Widerspruch.

GMzo
quelle
1
Die zweite Option ist nicht korrekt und daher spielt die Leistung keine Rolle . Dies beantwortet auch immer noch nicht die Frage, ob die Bestellung nach einer Zufallszahl akzeptabel, effizient ist oder wie sie funktioniert. Die erste Lösung hat auch Korrektheitsprobleme, aber sie sind keine so große Sache.
Servy
Entschuldigung, ich würde gerne wissen, was ein besserer Parameter für Quicksort von Linq OrderBy ist. Ich muss die Leistung testen. Ich denke jedoch, dass der Typ int nur eine bessere Geschwindigkeit als die Guid-Zeichenfolge hat, dies ist jedoch nicht der Fall. Ich habe verstanden, warum MSDN empfohlen hat. Die erste von der Lösung bearbeitete Leistung entspricht der von OrderBy mit zufälliger Instanz.
GMzo
Was bringt es, die Leistung von Code zu messen, der das Problem nicht löst? Die Leistung ist nur eine Überlegung zwischen zwei Lösungen , die beide funktionieren . Wenn Sie Lösungen arbeiten, dann können Sie beginnen , sie zu vergleichen.
Servy
Ich muss eine Zeit haben, um mehr Daten zu testen. Wenn sie fertig sind, verspreche ich, sie erneut zu veröffentlichen. Angenommen: Ich denke, Linq OrderBy ist nicht schlechter als die erste Lösung. Meinung: Es ist einfach zu bedienen und zu verstehen.
GMzo
Es ist merklich weniger effizient als sehr einfache, unkomplizierte Mischalgorithmen, aber auch hier spielt die Leistung keine Rolle . Sie mischen die Daten nicht zuverlässig und sind weniger leistungsfähig.
Servy