Search code examples
c#algorithmrandomshufflefisher-yates-shuffle

Fisher-Yates shuffle: Who is right, .NET 8 or Wikipedia?


Was looking for shuffling an array from within Unity, remembered that .NET 8 has Random.Shuffle.

It turns out that the implementation is the Fisher-Yates algorithm.

But when looking at it and at Wikipedia page, they're not 100% identical.

.NET 8 source:

public void Shuffle<T>(Span<T> values)
{
    int n = values.Length;

    for (int i = 0; i < n - 1; i++)
    {
        int j = Next(i, n);

        if (j != i)
        {
            T temp = values[i];
            values[i] = values[j];
            values[j] = temp;
        }
    }
}

Wikipedia, "inside-out" variant:

To initialize an array a of n elements to a randomly shuffled copy of source, both 0-based:
  for i from 0 to n − 1 do
      j ← random integer such that 0 ≤ j ≤ i
      if j ≠ i
          a[i] ← a[j]
      a[j] ← source[i]

The .NET version does everything within the if block, not Wikipedia's.

Question:

Which one of these is right and can you explain why it is?


Solution

  • As Wikipedia explains, this inside-out variant is initialising a target array that is different from the source array. The algorithm you quote from NET.8 is an inplace algorithm also described in the same Wikipedia article.

    When a different array must receive the shuffled data, then an assignment has to happen to each index of the target array. This is why that assignment is not part of the conditional block.

    The inplace algorithm can detect the situation where a value happens not to move and so the swap does not have to be executed.

    The NET.8 version corresponds to what Wikipedia calls "An equivalent version which shuffles the array in the opposite direction". Admittedly Wikipedia performs the swap unconditionally. Of course, if i and j are the same this swap is a no-op, and apparently NET.8 coders have found that it is worth it to perform an extra if comparison in each iteration so to save on executing a swap when both indices are equal.