The proposed shuffling algorithm is not correct.
While biases introduced by incorrect shuffling routines are sometimes very hard to discern (it might take large number of monte carlo simulations to reveal, looking at overall statical frequencies), in this case, the resulting problem is self evident. Repeatedly build an array of Array(0..<10)
, shuffle it, and print the results. The non-random behavior is apparent:
[8, 2, 1, 0, 3, 4, 5, 6, 7, 9]
[8, 1, 0, 2, 3, 4, 5, 6, 7, 9]
[8, 3, 1, 2, 0, 4, 5, 6, 7, 9]
[0, 8, 1, 2, 3, 4, 5, 6, 7, 9]
[8, 3, 1, 2, 0, 4, 5, 6, 7, 9]
[8, 9, 1, 2, 3, 4, 5, 6, 7, 0]
[8, 5, 1, 2, 3, 4, 0, 6, 7, 9]
[8, 4, 1, 2, 3, 0, 5, 6, 7, 9]
[8, 5, 1, 2, 3, 4, 0, 6, 7, 9]
[0, 8, 1, 2, 3, 4, 5, 6, 7, 9]
[8, 5, 1, 2, 3, 4, 0, 6, 7, 9]
[8, 7, 1, 2, 3, 4, 5, 6, 0, 9]
[8, 5, 1, 2, 3, 4, 0, 6, 7, 9]
[8, 2, 1, 0, 3, 4, 5, 6, 7, 9]
[8, 6, 1, 2, 3, 4, 5, 0, 7, 9]
[8, 2, 1, 0, 3, 4, 5, 6, 7, 9]
[0, 8, 1, 2, 3, 4, 5, 6, 7, 9]
[8, 7, 1, 2, 3, 4, 5, 6, 0, 9]
[8, 6, 1, 2, 3, 4, 5, 0, 7, 9]
[8, 7, 1, 2, 3, 4, 5, 6, 0, 9]
[8, 5, 1, 2, 3, 4, 0, 6, 7, 9]
[8, 2, 1, 0, 3, 4, 5, 6, 7, 9]
[8, 4, 1, 2, 3, 0, 5, 6, 7, 9]
[8, 4, 1, 2, 3, 0, 5, 6, 7, 9]
[8, 7, 1, 2, 3, 4, 5, 6, 0, 9]
[8, 5, 1, 2, 3, 4, 0, 6, 7, 9]
[8, 2, 1, 0, 3, 4, 5, 6, 7, 9]
[8, 4, 1, 2, 3, 0, 5, 6, 7, 9]
[8, 7, 1, 2, 3, 4, 5, 6, 0, 9]
[0, 8, 1, 2, 3, 4, 5, 6, 7, 9]
The first serious issue is that given that j
is set to 0
on every iteration of i
, the while j == i { ... }
obviously will not work for any i
greater than 0
.
Let us imagine that you fixed that issue (by replacing var j = 0
with var j = i
), a less obvious issue is that implicit in that while
logic is that you're effectively saying that element j
must be such that i < j < n
whereas the correct logic is i ≤ j < n
. Effectively, you're saying that element i
must be swapped with another element, whereas in a truly shuffled array, not swapping a particular element at all must be considered as an equally viable option. Frankly, a while
statement is not needed, anyway.
In short, it looks like you were attempting to perform a Fisher-Yates. You can replace var j = 0
and the subsequent while
loop with a single let
statement:
extension Array {
mutating func shuffle2() {
guard count > 1 else { return }
for i in 0 ..< count - 1 {
let j = Int.random(in: i ..< count) // or `Int(arc4random_uniform(UInt32(count - i))) + i`
swapAt(i, j)
}
}
}
Obviously, we would generally use the built in shuffling methods, shuffle
, but if writing your own, j
can be set with a single statement, without any while
loop.
For more information see How do I shuffle an array in Swift?