it-swarm.com.de

Die beste Möglichkeit, ein Array mit .NET zufällig zu erstellen

Was ist der beste Weg, um ein Array von Strings mit .NET zufällig zu erstellen? Mein Array enthält ungefähr 500 Zeichenfolgen, und ich möchte ein neues Array mit denselben Zeichenfolgen erstellen, jedoch in zufälliger Reihenfolge.

Bitte fügen Sie Ihrer Antwort ein C # -Beispiel hinzu.

118
Mats

Wenn Sie mit .NET 3.5 arbeiten, können Sie die folgende IEnumerable-Coolness verwenden (VB.NET, nicht C #, aber die Idee sollte klar sein ...):

Random rnd=new Random();
string[] MyRandomArray = MyArray.OrderBy(x => rnd.Next()).ToArray();    

Bearbeiten: OK und hier ist der entsprechende VB.NET-Code:

Dim rnd As New System.Random
Dim MyRandomArray = MyArray.OrderBy(Function() rnd.Next()).ToArray()

Zweite Bearbeitung, als Reaktion auf die Bemerkungen, dass System.Random "nicht threadsicher ist" und "nur für Spielzeug-Apps geeignet" ist, weil eine zeitbasierte Sequenz zurückgegeben wird: Wie in meinem Beispiel verwendet, ist Random () absolut Thread-sicher, sofern nicht Sie lassen die Routine, in der Sie das Array randomisieren, erneut eingegeben werden. In diesem Fall benötigen Sie sowieso etwas wie lock (MyRandomArray), um Ihre Daten nicht zu beschädigen, wodurch auch rnd geschützt wird. 

Es sollte auch klar sein, dass System.Random als Entropiequelle nicht sehr stark ist. Wie in der MSDN-Dokumentation erwähnt, sollten Sie etwas verwenden, das von System.Security.Cryptography.RandomNumberGenerator abgeleitet ist, wenn Sie sicherheitsrelevante Aktionen ausführen. Zum Beispiel:

using System.Security.Cryptography;

...

RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();
string[] MyRandomArray = MyArray.OrderBy(x => GetNextInt32(rnd)).ToArray();

...

static int GetNextInt32(RNGCryptoServiceProvider rnd)
    {
        byte[] randomInt = new byte[4];
        rnd.GetBytes(randomInt);
        return Convert.ToInt32(randomInt[0]);
    }
146
mdb

Die folgende Implementierung verwendet den Fisher-Yates-Algorithmus . Es läuft in O(n) - Zeit und mischt sich an Ort und Stelle, daher ist es besser als die 'sort by random'-Technik, obwohl es mehr Codezeilen gibt. Siehe hier für einige vergleichende Leistungsmessungen. Ich habe System.Random verwendet, was für nicht-kryptographische Zwecke in Ordnung ist. *

static class RandomExtensions
{
    public static void Shuffle<T> (this Random rng, T[] array)
    {
        int n = array.Length;
        while (n > 1) 
        {
            int k = rng.Next(n--);
            T temp = array[n];
            array[n] = array[k];
            array[k] = temp;
        }
    }
}

Verwendungszweck:

var array = new int[] {1, 2, 3, 4};
new Random().Shuffle(array);

* Bei längeren Arrays, um die (extrem große) Anzahl von Permutationen gleich wahrscheinlich zu machen, müsste ein Pseudo-Zufallszahlengenerator (PRNG) durch viele Iterationen für jeden Swap laufen, um genügend Entropie zu erzeugen. Bei einem 500-Elemente-Array ist nur ein sehr kleiner Bruchteil der möglichen 500! Permutationen können mit einem PRNG erhalten werden. Der Fisher-Yates-Algorithmus ist jedoch unvoreingenommen und daher ist der Shuffle genauso gut wie der RNG, den Sie verwenden.

181
Matt Howells

Sie suchen einen Mischalgorithmus, oder?

Okay, es gibt zwei Möglichkeiten, dies zu tun: Die Klugen, aber Leute, die scheinbar immer missverstanden werden - und - es wird falsch - so - vielleicht - es ist nicht so klug Weg, und die Dumm-wie-Felsen-aber-wen-kümmert-weil-es funktioniert so.

Dummer Weg

  • Erstellen Sie ein Duplikat Ihres ersten Arrays, aber markieren Sie jeden String mit einer Zufallszahl.
  • Sortiere das doppelte Array nach der Zufallszahl.

Dieser Algorithmus funktioniert gut, aber stellen Sie sicher, dass es unwahrscheinlich ist, dass Ihr Zufallszahlengenerator zwei Zeichenfolgen mit derselben Nummer markiert. Aufgrund des sogenannten Birthday Paradox passiert dies öfter als Sie vielleicht erwarten. Seine zeitliche Komplexität ist O (n log n).

Cleverer Weg

Ich werde das als rekursiven Algorithmus beschreiben:

Um ein Array der Größe n (Indizes im Bereich [0 ..n- 1]) zu mischen:

wenn n = 0
  • nichts tun
wenn n> 0
  • (rekursiver Schritt) mischt die ersten n- 1 Elemente des Arrays
  • wählen Sie einen zufälligen Index x im Bereich [0 ..n- 1]
  • tauschen Sie das Element am Index n- 1 mit dem Element am Index x aus.

Das iterative Äquivalent besteht darin, einen Iterator durch das Array zu führen und dabei mit zufälligen Elementen zu tauschen. Beachten Sie jedoch, dass Sie nicht mit einem Element after das Element wechseln können, auf das der Iterator zeigt. Dies ist ein sehr häufiger Fehler und führt zu einem voreingenommenen Shuffle.

Die zeitliche Komplexität beträgt O (n).

16
Pitarou

Dieser Algorithmus ist einfach, aber nicht effizient, O (N2). Alle "order by" -Algorithmen sind typischerweise O (N log N). Unter Hunderttausenden von Elementen macht es wahrscheinlich keinen Unterschied, aber für große Listen.

var stringlist = ... // add your values to stringlist

var r = new Random();

var res = new List<string>(stringlist.Count);

while (stringlist.Count >0)
{
   var i = r.Next(stringlist.Count);
   res.Add(stringlist[i]);
   stringlist.RemoveAt(i);
}

Der Grund, warum es O ist (N2) ist subtil: List.RemoveAt () ist eine O(N) - Operation, sofern Sie nicht in der richtigen Reihenfolge vom Ende entfernen.

8
Sklivvz

Sie können aus Matt Howells auch eine Erweiterungsmethode erstellen. Beispiel.

   namespace System
    {
        public static class MSSystemExtenstions
        {
            private static Random rng = new Random();
            public static void Shuffle<T>(this T[] array)
            {
                rng = new Random();
                int n = array.Length;
                while (n > 1)
                {
                    int k = rng.Next(n);
                    n--;
                    T temp = array[n];
                    array[n] = array[k];
                    array[k] = temp;
                }
            }
        }
    }

Dann kannst du es einfach so benutzen: 

        string[] names = new string[] {
                "Aaron Moline1", 
                "Aaron Moline2", 
                "Aaron Moline3", 
                "Aaron Moline4", 
                "Aaron Moline5", 
                "Aaron Moline6", 
                "Aaron Moline7", 
                "Aaron Moline8", 
                "Aaron Moline9", 
            };
        names.Shuffle<string>();
4
Aaron

Das Randomisieren des Arrays ist intensiv, da Sie sich um eine Reihe von Strings bewegen müssen. Warum nicht einfach zufällig aus dem Array lesen? Im schlimmsten Fall könnten Sie sogar eine Wrapper-Klasse mit getNextString () erstellen. Wenn Sie wirklich ein zufälliges Array erstellen müssen, können Sie so etwas tun

for i = 0 -> i= array.length * 5
   swap two strings in random places

Die * 5 ist willkürlich. 

1
stimms

Wenn ich nur aus meinem Kopf denke, könnten Sie Folgendes tun:

public string[] Randomize(string[] input)
{
  List<string> inputList = input.ToList();
  string[] output = new string[input.Length];
  Random randomizer = new Random();
  int i = 0;

  while (inputList.Count > 0)
  {
    int index = r.Next(inputList.Count);
    output[i++] = inputList[index];
    inputList.RemoveAt(index);
  }

  return (output);
}
1
Tarsier

Dieser Beitrag wurde bereits ziemlich gut beantwortet - verwenden Sie eine Durstenfeld-Implementierung des Fisher-Yates-Shuffles für ein schnelles und objektives Ergebnis. Es wurden sogar einige Implementierungen veröffentlicht, obwohl einige davon tatsächlich falsch sind.

Ich habe vor einiger Zeit ein paar Posts über die Implementierung von vollständigen und partiellen Shuffles mit dieser Technik geschrieben, und (dieser zweite Link ist, wo ich hoffe, dass ich einen Mehrwert erzielen kann), ein Folge-Post über das Überprüfen ob Ihre Implementierung unvoreingenommen ist , um einen Shuffle-Algorithmus zu überprüfen. Sie können am Ende des zweiten Postens die Auswirkung eines einfachen Fehlers in der Zufallszahlenauswahl sehen.

1
Greg Beech

Erzeugen Sie ein Array von zufälligen Floats oder Ints derselben Länge. Sortieren Sie dieses Array und führen Sie entsprechende Swaps für Ihr Zielarray durch.

Dies ergibt eine wirklich unabhängige Sortierung.

0
Nick
Random r = new Random();
List<string> list = new List(originalArray);
List<string> randomStrings = new List();

while(list.Count > 0)
{
int i = r.Random(list.Count);
randomStrings.Add(list[i]);
list.RemoveAt(i);
}
0
nullDev

Dies ist eine vollständige funktionierende Konsolenlösung basierend auf dem Beispiel, das hier bereitgestellt wird :

class Program
{
    static string[] words1 = new string[] { "brown", "jumped", "the", "fox", "quick" };

    static void Main()
    {
        var result = Shuffle(words1);
        foreach (var i in result)
        {
            Console.Write(i + " ");
        }
        Console.ReadKey();
    }

   static string[] Shuffle(string[] wordArray) {
        Random random = new Random();
        for (int i = wordArray.Length - 1; i > 0; i--)
        {
            int swapIndex = random.Next(i + 1);
            string temp = wordArray[i];
            wordArray[i] = wordArray[swapIndex];
            wordArray[swapIndex] = temp;
        }
        return wordArray;
    }         
}
0
usefulBee

Dieser Code mischt Zahlen in einem Array.

using System;

// ...
    static void Main(string[] args)
    {
        Console.ForegroundColor = ConsoleColor.Cyan;
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        Shuffle(numbers);

        for (int i = 0; i < numbers.Length; i++)
            Console.Write(numbers[i] + (i < numbers.Length - 1 ? ", " : null));
        Console.WriteLine();

        string[] words = { "this", "is", "a", "string", "of", "words" };
        Shuffle(words);

        for (int i = 0; i < words.Length; i++)
            Console.Write(words[i] + (i < words.Length - 1 ? ", " : null));
        Console.WriteLine();

        Console.ForegroundColor = ConsoleColor.Gray;
        Console.Write("Press any key to quit . . . ");
        Console.ReadKey(true);
    }

    static void Shuffle<T>(T[] array)
    {
        Random random = new Random();

        for (int i = 0; i < array.Length; i++)
        {
            T temporary = array[i];
            int intrandom = random.Next(array.Length);
            array[i] = array[intrandom];
            array[intrandom] = temporary;
        }
    }
0
Bilal

Sie brauchen keine komplizierten Algorithmen.

Nur eine einfache Zeile:

Random random = new Random();
array.ToList().Sort((x, y) => random.Next(-1, 1)).ToArray();

Beachten Sie, dass wir zuerst die Array in eine List konvertieren müssen, wenn Sie List nicht an erster Stelle verwenden.

Beachten Sie auch, dass dies für sehr große Arrays nicht effizient ist! Ansonsten ist es sauber und einfach.

0
bytecode77

Ok, das ist eindeutig ein Schlag von meiner Seite (entschuldigt sich ...), aber ich benutze oft eine recht allgemeine und kryptographisch starke Methode.

public static class EnumerableExtensions
{
    static readonly RNGCryptoServiceProvider RngCryptoServiceProvider = new RNGCryptoServiceProvider();
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> enumerable)
    {
        var randomIntegerBuffer = new byte[4];
        Func<int> Rand = () =>
                             {
                                 RngCryptoServiceProvider.GetBytes(randomIntegerBuffer);
                                 return BitConverter.ToInt32(randomIntegerBuffer, 0);
                             };
        return from item in enumerable
               let rec = new {item, rnd = Rand()}
               orderby rec.rnd
               select rec.item;
    }
}

Shuffle () ist eine Erweiterung für jedes IEnumerable, also können Zahlen von 0 bis 1000 in zufälliger Reihenfolge in einer Liste abgerufen werden

Enumerable.Range(0,1000).Shuffle().ToList()

Diese Methode wird auch beim Sortieren keine Überraschungen bereiten, da der Sortierwert genau einmal pro Element in der Sequenz erzeugt und gespeichert wird.

0
jlarsson

Jacco, Ihre Lösung für einen benutzerdefinierten IComparer ist nicht sicher. Die Sortierroutinen erfordern, dass der Komparator mehreren Anforderungen entspricht, damit er ordnungsgemäß funktioniert. An erster Stelle steht die Konsistenz. Wenn der Vergleicher für dasselbe Objektpaar aufgerufen wird, muss er immer dasselbe Ergebnis zurückgeben. (Der Vergleich muss auch transitiv sein).

Wenn diese Anforderungen nicht erfüllt werden, kann dies zu einer Reihe von Problemen in der Sortierroutine führen, einschließlich der Möglichkeit einer Endlosschleife. 

In Bezug auf die Lösungen, die jedem Eintrag einen zufälligen numerischen Wert zuordnen und dann nach diesem Wert sortieren, führen diese zu einer inhärenten Verzerrung der Ausgabe, da jedes Mal, wenn zwei Einträgen derselbe numerische Wert zugewiesen wird, die Zufälligkeit der Ausgabe beeinträchtigt wird. (In einer "stabilen" Sortierroutine wird, was immer zuerst in der Eingabe steht, zuerst in der Ausgabe angezeigt. Array.Sort ist nicht zufällig stabil, aber es gibt immer noch eine Abweichung, die auf der durch den Quicksort-Algorithmus durchgeführten Partitionierung beruht).

Sie müssen darüber nachdenken, welchen Grad an Zufälligkeit Sie benötigen. Wenn Sie eine Pokerseite betreiben, auf der Sie kryptografische Zufallsniveaus benötigen, um sich gegen einen entschlossenen Angreifer zu schützen, haben Sie ganz andere Anforderungen als jemand, der nur eine Song-Playlist randomisieren möchte.

Beim Mischen von Songlisten ist die Verwendung eines PRNG (wie System.Random) kein Problem. Für eine Pokerseite ist dies nicht einmal eine Option, und Sie müssen viel schwieriger über das Problem nachdenken, als es jemand mit Stackoverflow tun wird. (Die Verwendung eines kryptografischen RNG ist nur der Anfang. Sie müssen sicherstellen, dass Ihr Algorithmus keine Verzerrung einführt, dass Sie über ausreichende Entropiequellen verfügen und keinen internen Status offenlegen, der die zufällige Zufälligkeit beeinträchtigen könnte.).

0
Andrew
        int[] numbers = {0,1,2,3,4,5,6,7,8,9};
        List<int> numList = new List<int>();
        numList.AddRange(numbers);

        Console.WriteLine("Original Order");
        for (int i = 0; i < numList.Count; i++)
        {
            Console.Write(String.Format("{0} ",numList[i]));
        }

        Random random = new Random();
        Console.WriteLine("\n\nRandom Order");
        for (int i = 0; i < numList.Capacity; i++)
        {
            int randomIndex = random.Next(numList.Count);
            Console.Write(String.Format("{0} ", numList[randomIndex]));
            numList.RemoveAt(randomIndex);
        }
        Console.ReadLine();
0
Nitesh Katare