Fisher yates algorithm as described on wikipedia is
The algorithm produces an unbiased permutation: every permutation is equally likely.
I went through some articles that explains how a naive and fisher yates algorithm can produce biased and unbiased combination of items in a set.
Link to the articles
Fisher-Yates Shuffle – An Algorithm Every Developer Should Know
Randomness is hard: learning about the Fisher-Yates shuffle algorithm & random number generation
The articles goes on to show graphs of almost unbiased and very biased results with both these two algorithms. I tried to reproduce the probabilities but i can't seem to produce the difference.
Here is my code
import java.util.*
class Problem {
private val arr = intArrayOf(1, 2, 3)
private val occurrences = mutableMapOf<String, Int>()
private val rand = Random()
fun biased() {
for (i in 1..100000) {
for (i in arr.indices) {
val k = rand.nextInt(arr.size)
val temp = arr[k]
arr[k] = arr[i]
arr[i] = temp
}
val combination = arr.toList().joinToString("")
if (occurrences.containsKey(combination)) {
occurrences[combination] = occurrences[combination]!! + 1
} else {
occurrences[combination] = 1
}
}
print("Naive:\n")
occurrences.forEach { (t, u) ->
print("$t: $u\n")
}
}
/**
* Fisher yates algorithm - unbiased
*/
fun unbiased() {
for (i in 1..100000) {
for (i in arr.size-1 downTo 0) {
val j = rand.nextInt(i + 1)
val temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
val combination = arr.toList().joinToString("")
if (occurrences.containsKey(combination)) {
occurrences[combination] = occurrences[combination]!! + 1
} else {
occurrences[combination] = 1
}
}
print("Fisher Yates:\n")
occurrences.forEach { (t, u) ->
print("$t: $u\n")
}
}
}
fun main() {
Problem().biased()
Problem().unbiased()
}
This produces the following result
Naive:
312: 16719
213: 16654
231: 16807
123: 16474
132: 16636
321: 16710
Fisher Yates:
123: 16695
312: 16568
213: 16923
321: 16627
132: 16766
231: 16421
My results are not very different in both cases. My question is, is my implementation wrong? Or my understanding is wrong?
Your implementation of both algorithms has a mistake that smooths out the bias introduced by the naive shuffling. You don't start over with the same permutation for each shuffle, but with the one produced by the last shuffle. A simple fix is to reset the array to be
[1, 2, 3]
each time:Output:
Not a Kotlin-programmer, so there's probably a more elegant way to do this, but it's good enough, I guess.