it-swarm.com.de

Der effizienteste Weg, eine Liste von ganzen Zahlen in C # zufällig zu sortieren (Shuffle)

Ich muss eine Liste von Ganzzahlen (0-1999) auf eine möglichst effiziente Art und Weise zufällig sortieren. Irgendwelche Ideen?

Momentan mache ich so etwas:

bool[] bIndexSet = new bool[iItemCount];

for (int iCurIndex = 0; iCurIndex < iItemCount; iCurIndex++)
{
    int iSwapIndex = random.Next(iItemCount);
    if (!bIndexSet[iSwapIndex] && iSwapIndex != iCurIndex)
    {
        int iTemp = values[iSwapIndex];
        values[iSwapIndex] = values[iCurIndex];
        values[iCurIndex] = values[iSwapIndex];
        bIndexSet[iCurIndex] = true;
        bIndexSet[iSwapIndex] = true;
    }
}
51
Carl

Ein guter linearer Zeitmischungsalgorithmus ist der Fisher-Yates-Mischer .

Ein Problem, das Sie bei Ihrem vorgeschlagenen Algorithmus feststellen werden, ist, dass Ihre Schleife, wenn Sie sich dem Ende der Shuffle-Phase nähern, viel Zeit damit verbringen wird, nach zufällig ausgewählten Elementen zu suchen, die noch nicht ausgetauscht wurden. Dies kann eine unbestimmte Zeit in Anspruch nehmen, sobald das letzte zu tauschende Element erreicht ist.

Es sieht auch so aus, als würde Ihr Algorithmus niemals enden, wenn eine ungerade Anzahl von zu sortierenden Elementen vorhanden ist.

53
Greg Hewgill
static Random random = new Random();

public static IEnumerable<T> RandomPermutation<T>(IEnumerable<T> sequence)
{
    T[] retArray = sequence.ToArray();


    for (int i = 0; i < retArray.Length - 1; i += 1)
    {
        int swapIndex = random.Next(i, retArray.Length);
        if (swapIndex != i) {
            T temp = retArray[i];
            retArray[i] = retArray[swapIndex];
            retArray[swapIndex] = temp;
        }
    }

    return retArray;
}

geändert, um Listen oder andere Objekte zu behandeln, die IEnumerable implementieren

30
ICR

Wir können daraus eine Erweiterungsmethode machen, um einen Random-Enumerator für jede IList-Sammlung zu erhalten

class Program
{
    static void Main(string[] args)
    {
        IList<int> l = new List<int>();
        l.Add(7);
        l.Add(11);
        l.Add(13);
        l.Add(17);

        foreach (var i in l.AsRandom())
            Console.WriteLine(i);

        Console.ReadLine();
    }
}


public static class MyExtensions
{
    public static IEnumerable<T> AsRandom<T>(this IList<T> list)
    {
        int[] indexes = Enumerable.Range(0, list.Count).ToArray();
        Random generator = new Random();

        for (int i = 0; i < list.Count; ++i )
        {
            int position = generator.Next(i, list.Count);

            yield return list[indexes[position]];

            indexes[position] = indexes[i];
        }
    }
}   

Dies verwendet eine umgekehrte Fisher-Yates-Zufallswiedergabe für die Indizes der Liste, die wir zufällig auflisten möchten. Es ist ein bisschen groß (Zuweisung von 4 * list.Count-Bytes), läuft aber in O (n). 

18
foson

Wie Greg darauf hinwies, wäre der Fisher-Yates-Shuffle der beste Ansatz. Hier ist eine Implementierung des Algorithmus von Wikipedia:

public static void shuffle (int[] array)
{
   Random rng = new Random();   // i.e., Java.util.Random.
   int n = array.length;        // The number of items left to shuffle (loop invariant).
   while (n > 1)
   {
      int k = rng.nextInt(n);  // 0 <= k < n.
      n--;                     // n is now the last pertinent index;
      int temp = array[n];     // swap array[n] with array[k] (does nothing if k == n).
      array[n] = array[k];
      array[k] = temp;
   }
}

Die Implementierung basiert auf Random.nextInt (int) liefert ausreichend zufällig und unvoreingenommen Ergebnisse

5
Micah

Ich bin nicht sicher, was den Wirkungsgrad angeht, aber ich habe etwas Ähnliches verwendet, wenn Sie nicht gegen eine ArrayList sind:

private ArrayList ShuffleArrayList(ArrayList source)
{
    ArrayList sortedList = new ArrayList();
    Random generator = new Random();

    while (source.Count > 0)
    {
        int position = generator.Next(source.Count);
        sortedList.Add(source[position]);
        source.RemoveAt(position);
    }

    return sortedList;
}

Auf diese Weise müssen Sie sich nicht um den Zwischentausch kümmern.

4
Joseph Ferris

Um Ihre Effizienz zu verbessern, können Sie eine Reihe von Werten/Indizes beibehalten, die ausgetauscht wurden, und keine Boolean-Werte, um anzuzeigen, dass sie getauscht wurden. Wählen Sie Ihren randomisierten Swap-Index aus dem verbleibenden Pool aus. Wenn der Pool 0 ist oder wenn Sie die Anfangsliste durchlaufen haben, sind Sie fertig. Sie haben nicht die Möglichkeit, einen zufälligen Swap-Indexwert auszuwählen. 

Wenn Sie einen Swap durchführen, entfernen Sie sie einfach aus dem Pool.

Für die Größe der Daten, die Sie betrachten, ist dies keine große Sache.

2
Tim

Die Antwort von ICR ist sehr schnell, aber die resultierenden Arrays werden nicht normal verteilt. Wenn Sie eine Normalverteilung wünschen, hier der Code:

    public static IEnumerable<T> RandomPermutation<T>(this IEnumerable<T> sequence, int start,int end)
    {
        T[] array = sequence as T[] ?? sequence.ToArray();

        var result = new T[array.Length];

        for (int i = 0; i < start; i++)
        {
            result[i] = array[i];
        }
        for (int i = end; i < array.Length; i++)
        {
            result[i] = array[i];
        }

        var sortArray=new List<KeyValuePair<double,T>>(array.Length-start-(array.Length-end));
        lock (random)
        {
            for (int i = start; i < end; i++)
            {
                sortArray.Add(new KeyValuePair<double, T>(random.NextDouble(), array[i]));
            }
        }

        sortArray.Sort((i,j)=>i.Key.CompareTo(j.Key));

        for (int i = start; i < end; i++)
        {
            result[i] = sortArray[i - start].Value;
        }

        return result;
    }

Beachten Sie, dass dieser Algorithmus in meinen Tests 6-mal langsamer war als der ICR-Algorithmus. Dies ist jedoch die einzige Möglichkeit, eine normale Ergebnisverteilung zu erhalten

1
Arsen Zahray
itemList.OrderBy(x=>Guid.NewGuid()).Take(amount).ToList()
1
Dmitry

wie wäre es mit :

System.Array.Sort(arrayinstance, RandomizerMethod);
...
//any evoluated random class could do it !
private static readonly System.Random Randomizer = new System.Random();

private static int RandomizerMethod<T>(T x, T y)
    where T : IComparable<T>
{
    if (x.CompareTo(y) == 0)
        return 0;

    return Randomizer.Next().CompareTo(Randomizer.Next());
}

voila!

0
que dal

Würde so etwas nicht funktionieren?

var list = new[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
var random = new Random();
list.Sort((a,b)=>random.Next(-1,1));
0
Dan

Ich denke, die letzten beiden Zeilen müssen in Micahs Antwort ausgetauscht werden. So könnte der Code aussehen

 public static void shuffle(int[] array) {
        Random rng = new Random();   // i.e., Java.util.Random.
        int n = array.Length;        // The number of items left to shuffle (loop invariant).
        while (n > 1) {
            int k = rng.Next(n);  // 0 <= k < n.
            n--;                     // n is now the last pertinent index;
            int temp = array[n];     // swap array[n] with array[k] (does nothing if k == n).
            array[n] = array[k];
            array[k] = temp;

        }
    }
0
nKnight