• JS更随机的随机数


    一.问题背景

    一个二维平面上有一群NPC,每一回合可以随机向上/下/左/右任一方向走1步,有单位碰撞体积(NPC位置不能重合)

    规则就这么简单,初始情况下这群NPC是被人工均匀分布在二维平面上的,运行N个回合后发现所有NPC都集中在了左下角。。怎么会这样,说好的随机呢?

    二.分析

    现有的实现是这样的:

    1. 根据NPC的当前位置判断得到可以去的位置,把结果存放在一维数组arr里

      P.S.上/下/左/右最多4个点(周围空荡荡的),最少0个点(被围起来了)

    2. 生成[0, arr.length – 1]内的一个随机数,作为目标位置索引值index

      // 生成随机数[min, max]
      w.Util.rand = function(min, max) {
      
          return Math.round(Math.random() * (max - min) + min);
      }
    3. 控制NPC移动到arr[index]的位置

    逻辑应该是没有问题的,可是为什么运行结果是NPC都跑到左下角开会去了呢?等等,为什么是左下而不是其它角角?

    因为实现第一步的时候是按照上 -> 下 -> 左 -> 右的顺序判断的,最后都去了左下角,说明向上和向右的概率太小了(生成随机数的函数rand是没问题的,确实能得到[min, max]的数)

    问题的根源是Math.random()不给力,生成[0, 1)之间的小数,取到0和靠近1的值概率很小,所以rand()函数生成的随机数取到min和mix的概率也很小,向上/向右的可能性也就小了

    三.解决方案

    既然取到min和max的概率很小,中间概率比较均匀,那好办,切掉这两个值就好了。具体实现如下:

    function randEx(min, max) {
    
        var num;
        var maxEx = max + 2; // 扩大范围到[min, max + 2],引入两个多余值替换min/max
    
        do{
    
            num = Math.round(Math.random() * (maxEx - min) + min);
            num--;
        } while (num < min || num > max);   // 范围不对,继续循环
    
        return num;
    }

    值得一提的是上面的加2减1比较巧妙,能够恰好排除min和max

    四.运行结果

    JS中Math.random()返回值的概率差异可能比你想象的要大些,在实际应用中是不可接受的

    测试代码如下:

    // 生成随机数[min, max]
    w.Util.rand = function(min, max) {
    
        return Math.round(Math.random() * (max - min) + min);
    }
    
    // 生成更随机的随机数[min, max]
    function randEx(min, max) {
    
        var num;
        var maxEx = max + 2; // 扩大范围到[min, max + 2],引入两个多余值替换min/max
    
        do{
    
            num = Math.round(Math.random() * (maxEx - min) + min);
            num--;
        } while (num < min || num > max);   // 范围不对,继续循环
    
        return num;
    }
    
    function testRand(times, fun) {
    
        var arr = [1, 2, 3, 4];
        var count = [];
        var randVal;
        for (var i = 0; i < times; i++) {
    
            // 获取[0, 3]的随机数
            randVal = fun(0, arr.length - 1);
            // 记录次数
            if (typeof count[randVal] !== "number") {
    
                count[randVal] = 0;
            }
            count[randVal]++;
        }
    
        console.log(count);
    }
    
    console.log("100 times");
    testRand(100, w.Util.rand); // 之前的实现
    testRand(100, randEx);      // 改进过的实现
    
    console.log("1000 times");
    testRand(1000, w.Util.rand); // 之前的实现
    testRand(1000, randEx);      // 改进过的实现
    
    console.log("10000 times");
    testRand(10000, w.Util.rand); // 之前的实现
    testRand(10000, randEx);      // 改进过的实现
    
    console.log("100000 times");
    testRand(100000, w.Util.rand); // 之前的实现
    testRand(100000, randEx);      // 改进过的实现

    运行结果如下:

    random

    看到了吧,效果还是很不错的

    后话

    理论上Java的Math.random(),C#的next可能也存在这个问题,这里就没有必要验证了,因为randEx函数的思想(加2减1,嫌效果不好还可以加4减2、加6减3……直到满意为止)是通用的

  • 相关阅读:
    python接口自动化5-Json数据处理
    python接口自动化4-绕过验证码登录(cookie)
    python接口自动化2-发送post请求
    python接口自动化1-发送get请求
    python+selenium个人学习笔记11-登录封装与调用
    [jzoj]5257.小X的佛光
    [jzoj]1417.数学题
    2017.08.15【NOIP提高组】模拟赛B组
    [jzoj]1383.奇怪的问题
    [jzoj]1229.Hanoi
  • 原文地址:https://www.cnblogs.com/ayqy/p/4511565.html
Copyright © 2020-2023  润新知