1

I came across the algorithm below for shuffling an array in Javascript. It seems to differ from the Fisher–Yates shuffle in that the range of available "swaps" increases with the for-loop counter. This appears to be the opposite of how the Fisher-Yates version behaves. I'm curious as to whether this is a valid algorithm. Is it the Fisher-Yates in disguise? Is it biased?

If anyone could provide some code to test the frequency of the permutations it generates that would be a bonus.

<script>
var shuffle = function (myArray) {
    var random = 0;
    var temp = 0;
    console.log(myArray);
    for (i = 1; i < myArray.length; i++) {
        random = Math.round(Math.random() * i);
        console.log(random + '\n');
        temp = myArray[i];
        myArray[i] = myArray[random];
        myArray[random] = temp;
        console.log(myArray);
    }
    return myArray;
}

var arr = [1, 2, 3, 4];

shuffle(arr);

</script>
6
  • Have you tested it to see if there's any observable bias? Commented Aug 17, 2016 at 7:15
  • That random indentation, though... Commented Aug 17, 2016 at 7:16
  • 1
    is this a valid algorithm - Yes, it does not throw any errors. Commented Aug 17, 2016 at 7:18
  • Not sure how to write a program to test all the permutation for a large number of trials. The results "look" fair at first glance, but I want more evidence - having read about the gross bias of the common naive solution to shuffling. Commented Aug 17, 2016 at 7:21
  • use Math.floor() instead of Math.round(), besides of that is the algorithm good and pretty common. edit: the implementations I know always iterate backwards; can't tell wether this has a significant difference. Commented Aug 17, 2016 at 7:22

1 Answer 1

3

No, it's not a fair shuffle.

Math.random() * i is a uniform random floating point value between 0 and i, but Math.round(Math.random() * i) does not pick integers between 0 and i equally. For example, when i is 2, the values in the range [0, 0.5) round to 0, values in the range [0.5, 1.5) round to 1, and values in the range (1.5, 2) round to 2. That means instead of picking 0, 1 and 2 equally often, 1 is picked with probability 0.5, and 0 and 2 are picked with probability 0.25 each.

Math.floor(Math.random * (i + 1)) would be correct.

You can verify this statistically: shuffle the array [0, 1, 2] 10000 times and see how often 2 remains at the end of the array. It should be around 3333, but because of the bias it'll be more like 2500.

Other than that, the algorithm is correct and could be described as a Fisher-Yates in reverse. You can prove it correct by induction. The base case of n=1 is trivial. The induction step is also relatively easy: if you've got a uniformly random permutation of n items, and then insert the n+1'th item at a uniformly random index from 0 to n+1, then you've got a random permutation of n+1 items.

Sign up to request clarification or add additional context in comments.

2 Comments

OK I accept the floor vs round part. However if it were to use floor, would it then be fair? It's the fact that the range of swapping positions starts small and then grows which seems radically different from the Fisher-Yates.
@Robin yes, I expanded my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.