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);      // 改进过的实现

运行结果如下:

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

后话

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

时间: 2024-10-11 00:31:20

JS更随机的随机数的相关文章

外贸建站JS控制随机显示内容代码分享

外贸建站JS控制随机显示内容代码分享 <script> $(function(){ $('li[id^=p_]').each(function(){ var this_id = $(this).attr("id"); var id_arr = this_id.split('_'); if(id_arr[1]>9){ $(this).hide(); } }) }) function tryLuck(){ var num=30; var p_list=new Array(

js实现随机选取[10,100)中的10个整数,存入一个数组,并排序。 另考虑(10,100]和[10,100]两种情况。

1.js实现随机选取[10,100)中的10个整数,存入一个数组,并排序. 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script type="text/java

js获取100个随机数存入数组

. //js获取100个随机数存入数组 $(function () { var arr = []; var num = 0; var str = ""; while (num < 100) { var n = GetRandom(0, 1000); if (jQuery.inArray(n, arr) == -1) { arr[num] = n; num++; } } var arr2 = arr.sort(); for (var i = 0; i < arr2.lengt

js生成随机固定长度字符串的简便方法

概述 碰到一个需求:用js生成固定长度的字符串.在网上查了很多资料,网上的方法都比较麻烦.我自己灵光一现,实现了一个比较简单的方法.记录下来,供以后开发时参考,相信对其他人也有用. js生成随机字符串 js生成随机字符串有一个奇妙的写法: //输出随机字符串 const randStr = () => Math.random().toString(36).substr(2); 浏览器开发者工具输入5次,输出如下: "4cc9gd4sbwd" "ox9r8g6g7h&qu

js里面随机抽取n个随机数

function getImageRandomPosition(){ do { var n = Math.floor(Math.random() * 12);//n为随机出现的0-11之内的数值 for (var i = 0; i < posArray.length; i++) { if (n == posArray[i]) { /*若n和数组里面的数值有重复,立即跳出函数*/ break; } } /*若n和数组里的数组无重复,那么i和数组的长度是相同的,这样可以避免出现重复的数字*/ if

JS 随机生成随机数 数组

function randomNum(iAll, iNow){ var arr = []; var nArr = []; for(var i = 1;i <= iAll; i++){ arr.push(i); } for(var i = 0; i< iNow; i++){ nArr.push(arr.splice(Math.floor(Math.random()*arr.length),1)); } return nArr; }

利用随机函数生成随机数

给定一个rand(),可以产生从0到RAND_MAX的随机数,其中RAND_MAX很大(常见值:16位int能表示的最大整数32767),写出利用rand()生成[a,b]中任意整数的函数,其中a>=0, b<=RAND_MAX,且b-a<<RAND_MAX. 分析: 这是在编程工作最常见的随机函数的应用,在这里做一个起点再合适不过.把随机数区间的起点从0变为a,同时把一共RAND_MAX+1个数的区间缩小至只含有b-a+1个数的区间,写为 a + rand()%(b-a+1),此

通过js实现随机生成图片

这次给大家分享一个通过js向HTML添加便签,实现随机代码生成的案例,代码已经放在下方,这里我在下面准备了50张图片,但是没有放在博文中,如果读者想要练习,可以自己下载一些图片,建议下载的多一些. <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="

JS生成随机的由字母数字组合的字符串

前言 最近有个需求,是需要生成3-32位长度的字母数字组合的随机字符串,另一个是生成43位随机字符串. 方法一 奇妙的写法 1 Math.random().toString(36).substr(2); 输出结果 解释 很有意思,研究了一下,基本上toString后的参数规定可以是2-36之间的任意整数,不写的话默认是10(也就是十进制),此时返回的值就是那个随机数. 若是偶数,返回的数值字符串都是短的,若是奇数,则返回的将是一个很大长度的表示值. 若<10 则都是数字组成,>10 才会包含字