错误的洗牌算法

最近看到一篇文章,上面介绍了12种JavaScript技巧,其中最后一种介绍了数组元素的洗牌,代码很简单

var list = [1,2,3];
console.log(list.sort(function() { Math.random() - 0.5 })); // [2,1,3]

乍一看觉得这段代码很精髓,简单明了又不乏智慧,兴奋的我甚至还记在了本子上。直到我看到了底下面一行评论,说这段代码has a mistake。处于好奇,我就点了他附带的链接

这篇文章毫不客气地提出观点:以上代码看似巧妙利用了 Array.prototype.sort 实现随机,但是,却有非常严重的问题,甚至是完全错误

我当时就震了个惊啊~然后不服气的把它的测试代码拿来写了个测试用例:

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script src="shuffle.js"></script>
    </head>
    <body>
        洗牌算法
    </body>
    <script>
        var array = [0,1,2,3,4,5,6,7,8,9];
        var result = [0,0,0,0,0,0,0,0,0,0];
        var times = 1000000;
        for (var i = 0; i < times; i++) {
            var sorted = shuffle(array.slice(0)); //arr如果不用copy的话,上一次洗牌的值会覆盖当前的arr。
            sorted.forEach(function(val,i){
                result[i]+=val;
            });
        }
        result = result.map(function(val){
            return val/t;
        });

        console.log(result);

    </script>
</html>

var sorted = shuffle(array.slice(0));关于这一行我一开始还很疑惑,干嘛要slice一下,直接用array的结果明明就是正确的。如图1。
图一
我就觉得肯定是楼主装逼写的这篇文章。

然后接着看他的其他分析,他用js实现了3种排序算法,冒泡,插入和快速排序。其中冒泡和插入的时间复杂度相同,而插入排序比较的次数要相对少一点。我分别运行了这些代码,发现结果真的如他分析的规律一样:

  • 冒泡的后面比前面大:
    这里写图片描述
  • 插入的前面比后面大:
    这里写图片描述
  • 快速的则没有什么规律:
    这里写图片描述

注:以上结果都是基于arry.slice(0)的

然后我就开始质疑自己开始的判断了,把这些排序都去了,改成arry自带的sort方法,然后打断点调试,发现如果不用用arry.slice(0)的话,每次shuffle后,array对象都会被改变,这样原始的顺序[0~9]的顺序就被打乱了,这样就真的相当于随机洗牌了。难怪图1的结果是正确的。
但是,我要测的是算法啊,总不能还依赖于用户怎么调用算法啊。要是别人不用array,那不是咖喱给给了。。

最后,我又测试了一下他写的那个 经典的随机排列,代码贴一下,加深自己记忆:

function shuffle(arr){
    var len = arr.length;
    for(var i =0 ;i <len -1 ;i++){
        var index = Math.floor(Math.random()*(len-i));
        /*
         *从前 len - i 个元素里随机一个位置,将这个元素和第 len - i 个元素进行交换
         */
        var temp = arr[index];
        arr[index] = arr[len-i-1];
        arr[len-i-1] = temp;
    }
    return arr;
}

最后他给的随机性的数学归纳法证明彻底征服了我,有兴趣可以查看原文。然后我表示我是跪着写完这篇博客的。。


总结

这个算法的确是彻底错误的。
看到别人的代码,不要盲目崇拜,要多想多质疑。这样才能提高啊。。

猜你喜欢

转载自blog.csdn.net/gunner6/article/details/51482819