zoukankan      html  css  js  c++  java
  • javascript专题系列--js乱序

    乱序的意思想必没有不知道:就是将数组打乱。
    听到乱序一般都会想到js的随机函数Math.random();

    var values = [1, 2, 3, 4, 5];
    values.sort(function() {
        return Math.random() - 0.5;
    });
    console.log(values)
    利用数组的sort方法,判断随机出来的0~1值与0.5的大小,实现排序。
    看似一个很不错的方案,代码逻辑也没毛病,一般情况下也确实能够做到乱序。但是,这是一个伪排序,是的还有但是(我也是今天才知道的,不求甚解的毛病啊~),为什么呢?先看看这个乱序的结果吧:
    var times = [0, 0, 0, 0, 0];
    for (var i = 0; i < 100000; i++) {
        let arr = [1, 2, 3, 4, 5];
        arr.sort(() => Math.random() - 0.5);
        times[arr[4] - 1]++;
    };
    console.log(times)

    测试的原理是:将[1, 2, 3, 4, 5]乱序10万次,计算乱序后数组的最后一个元素是1,2,3,4,5的次数分别是多少。

    运行几次得到的结果为:
     
     
     
    由这几次运行得到的结果可以看出:2出现的最后的次数明显少于其他数字,不是随机吗?按理说概率应该是相差不多才对啊!
    其实问题是在sort方法,各个浏览器对sort的实现方式不一样。
     
    Chrome的sort
    基于V8引擎,它的排序算进行了很多的优化,但是核心是小于等于10的数组用插入排序(稳定),大于10的采用了quickSort(不稳定)
     
    FireFox的sort
    基于SpiderMonkey引擎,采用了归并排序(稳定)
     
    Safari的sort
    基于Nitro(JavaScriptCore )引擎,如果没有自定义的排序规则传入,采用桶排序(不一定稳定, 桶排序的稳定性取决于桶内排序的稳定性, 因此其稳定性不确定。),传入自定义规则,采用归并排序(稳定)
     
    Microsoft Edge/IE9+
    基于Chakra引擎,采用快排(不稳定)
     
    关于sort的不同具体说明见:https://zhuanlan.zhihu.com/p/33260052
     
    以下用chrome测试乱序各种结果的概率:
    var times = 100000;
    var res = {};
    for(var i = 0; i < times; i++){
        var arr = [1, 2, 3];
        arr.sort(() => Match.random() - 0.5);
        var key = JSON.stringify(arr);
        res[key] ? res[key]++ : res[key] = 1;
    }
    
    // 为了方便展示,转换成百分比
    for (var key in res) {
        res[key] = res[key] / times * 100 + '%';
    }
    console.log(res);
    所得结果如下:
     
    几种结果出现的概率相差很大...所以说不是一个真正的乱序。
     
    Fisher-Yates算法【也叫“洗牌算法”】:为什么叫 Fisher–Yates 呢? 因为这个算法是由 Ronald Fisher 和 Frank Yates 首次提出的。代码如下:
    function shuffle(a) {
        var j, x, i;
        for (i = a.length; i; i--) {
            j = Math.floor(Math.random() * i);
            x = a[i-1];
            a[i - 1] = a[j];
            a[j] = x;
        }
        return a;
    }
    其原理就是:遍历数组元素,然后将当前元素与以后随机位置的元素进行交换,这样乱序更加彻底。
    如果用ES6的写法还能精简成:
    function shuffle(a) {
        for(let i = a.length; i; i--) {
            let j = Math.floor(Math.random() * i);
            [a[i - 1], a[j]] = [a[j], a[i - 1]];
        }
        return a;
    }

    再用上面的demo测试一下:
    var times = 100000;
    var res = {};
    
    for (var i = 0; i < times; i++) {
        var arr = shuffle([1, 2, 3]);
    
        var key = JSON.stringify(arr);
        res[key] ? res[key]++ :  res[key] = 1;
    }
    
    // 为了方便展示,转换成百分比
    for (var key in res) {
        res[key] = res[key] / times * 100 + '%'
    }
    
    console.log(res)
    得到结果如下:

    各种结果的概率都基本相同了,所以真正实现了乱序的效果!

     
    阅读Javascript专题之乱序:https://github.com/mqyqingfeng/Blog/issues/51 之笔记

  • 相关阅读:
    Cookie与session
    orm之中介模型
    mysql数据库基础命令——幕布链接
    css——幕布链接
    HTML基础——幕布链接
    js基础——幕布
    三层结构与MVC
    [C++]3-1 得分(Score ACM-ICPC Seoul 2005,UVa1585)
    [C++]环状序列(CircularSequence,ACM/ICPC Seoul 2004,UVa1584)
    [C++]猜数字游戏的提示(Master-Mind Hints,UVa340)
  • 原文地址:https://www.cnblogs.com/garfieldzhong/p/11932917.html
Copyright © 2011-2022 走看看