上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

[JavaScript基础] 随机整数的生成

[复制链接]
  • TA的每日心情
    难过
    2024-4-24 18:57
  • 签到天数: 13 天

    [LV.3]偶尔看看II

    19

    主题

    27

    回帖

    259

    积分

    荣誉开发者

    积分
    259

    荣誉开发者油中2周年生态建设者喜迎中秋

    发表于 2023-11-8 23:19:40 | 显示全部楼层 | 阅读模式

    本帖最后由 Yiero 于 2023-11-9 14:28 编辑

    随机整数的生成

    Math.random() 函数会随机生成 [0, 1) 中间的任意一个小数(包括0不包括1) , 通过这个性质我们可以做一个类似于百分比的计算:

    百分比计算

    目标: 生成 [0, 40) 的随机整数

    现在随便调用一次 Math.random() // -> 0.6443368920606216 , 我们暂且将其视为 64% .

    因为我们要生成 [0, 40) 的随机整数, 那么我们可以计算: 40 * 64% = 25.6 ≈ 25 .

    于是这一次随机中, 我们随机生成的数字就是 25.

    基于这个逻辑, 我们可以写出以下代码:

    const randomNumber = Math.random();                 // -> 0.6443368920606216
    const randomFloat = 40 * randomNumber;          // -> 25.773475682424866
    const randomInt = Math.floor(randomFloat);  // -> 25

    关于这里的为什么使用的是向下取整 Math.floor() 而不是四舍五入 Math.round() , 后文会再进行解释.


    更加通用的百分比计算

    由于上文中生成的数字比较的简单并且是整数, 所以计算逻辑会比较的简单, 为了更加的通用, 我们换一个场景:

    目标: 生成 [10, 45) 的随机整数

    虽然换了一个场景, 但是逻辑是一样, 还是一样的百分比问题.

    我们可以将最小数 10 提取出来, 然后将这个问题看成是一个生成 [0, 35) 的随机整数的问题, 然后我们就可以使用上文的逻辑去解决问题了:

    const randomNumber = Math.random();                         // -> 0.9273800879651488
    const randomFloat = (45 - 10) * randomNumber;       // -> 32.45830307878021
    const randomInt = Math.floor(randomFloat);          // -> 32
    
    // 最终生成的随机整数是 42
    const resultInt = 10 + randomInt;                               // 42

    包含最大值的随机数生成

    在前文中我们生成的都是不包括最大数的随机整数, 那么我们该如何生成包括最大数的随机整数呢?

    首先我们要讲到为什么之前的算法逻辑不能生成包括最大数的随机整数, 这跟上文我们提到的不使用四舍五入有关.

    JavaScript中的取整算法

    还是上文的百分比计算法, Math.random() 可以输出 0% ~ 99.999% 的值, 虽然不能直接输出 1 但是无伤大雅因为它无限接近 1 .

    这主要是三个取整方法的区别: 向下取整 Math.floor() , 四舍五入 Math.round()向上取整 Math.ceil() .

    这里我们直接把百分比拿来举例: 总共有 0% , 10% , 20% , ... , 80% , 90% , 100% 这11个整值百分比是我们需要的值, 然后 Math.random() 能够输出的是 0 ~ 99.999% .

    假设取整函数是十位数取整, (四舍五入情况下)也就是 0% ~ 4% 取整到 0% ; 5% ~ 14% 取整到 10% , 15% ~ 24% 取整到 20% , ... , 95% ~ 100% 取整到 100% .

    • 使用向下取整, 能得到都是 10% 概率拿到的 0% , 10% , ... ,90% 的十个整值百分比 (除了 100% 都能拿到, 也就是没有最大值);
    • 使用向上取整, 可以拿到都是 10% 概率拿到的 10% , ... ,90% , 100% 的十个整值百分比 (除了 0% 都能拿到, 也就是没有最小值);
    • 使用四舍五入, 可以有 5% 概率拿到 0% , 有 5% 概率获取到 100% , 剩下的9个值都是 10% 的概率获取到 (11个整值百分比都能获取到, 但是概率不能均分).

    通过下面这个函数, 我们可以验证具体情况是否是上文所述:

    /**
     * @param {number[]} numberList 需要统计数字数组
     * @Return {{[num: string]: string}} 返回数字数组中每个数字出现的占比(百分比)
     */
    const calc = (numberList) => {
        const result = {}
    
        numberList.forEach((item) => {
            result[item] = result[item] ? result[item] + 1 : 1;
        });
    
        const values = Object.values(result);
        const sum = values.reduce((total, current) => total += current, 0);
        for (let resultKey in result) {
            result[resultKey] = Math.round(result[resultKey] / sum * 100) + '%'
        }
    
        return result;
    }
    function generate(min, max, getIntMethod) {
        let getInt = Math.floor;
        switch (getIntMethod) {
            case 'floor':
                getInt = Math.floor;
                break;
            case 'ceil':
                getInt = Math.ceil;
                break;
            case 'round':
                getInt = Math.round;
                break;
        }
    
        const floorList = [];
        for (let i = 0; i < 100000; i++) {
            floorList.push(getInt((max - min) * Math.random()) + min)
        }
        return floorList;
    }
    
    console.log('floor: ',calc(generate(1, 5, 'floor')));
    // -> floor:  { '1': '25%', '2': '25%', '3': '24%', '4': '25%' }
    
    console.log('round: ',calc(generate(1, 5, 'round')));
    // -> round:  { '1': '12%', '2': '25%', '3': '25%', '4': '25%', '5': '12%' }
    
    console.log('ceil: ',calc(generate(1, 5, 'ceil')));
    // -> ceil:  { '2': '25%', '3': '25%', '4': '25%', '5': '25%' }

    提升区间

    上文提到, 只有 Math.floor()Math.ceil() 才能让生成的随机数概率均分, 但是使用这两个函数又无法取到某一个最值, 那么该如何解决呢?

    答案是提升我们取值的区间: 比如当我们想要获取到 [0, 35] 的所有值时, 使用 Math.floor(35 * Math.random()) 只能获取到 [0, 34] 的值, 那么我们将最大值加一位, 也就是使用 Math.floor((35 + 1) * Math.random()) , 就能够获取 [0, 35] 的值了.

    随机生成整数 randomInt 函数

    将上文提到的具体数字抽象成两个变量, min 表示最小值, max 表示最大值即可生成 [min, max] 的值.

    const randomInt = (min, max) => {
      return Math.floor((max - min + 1) * Math.random()) + min;
    }

    测试

    const floorList = [];
    
    for (let i = 0; i < 100000; i++) {
        floorList.push(randomInt(1, 9))
    }
    
    // 使用上文提到的 clac 函数
    console.log(calc(floorList));
    /* -> 
    {
      '1': '11%',
      '2': '11%',
      '3': '11%',
      '4': '11%',
      '5': '11%',
      '6': '11%',
      '7': '11%',
      '8': '11%',
      '9': '11%'
    }
    */
  • TA的每日心情
    开心
    2024-3-13 10:14
  • 签到天数: 211 天

    [LV.7]常住居民III

    296

    主题

    4002

    回帖

    3894

    积分

    管理员

    积分
    3894

    管理员荣誉开发者油中2周年生态建设者喜迎中秋油中3周年挑战者 lv2

    发表于 2023-11-9 09:51:34 | 显示全部楼层

    我这里是这样的

    image.png

    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。/ 微信公众号:一之哥哥
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-13 10:14
  • 签到天数: 211 天

    [LV.7]常住居民III

    296

    主题

    4002

    回帖

    3894

    积分

    管理员

    积分
    3894

    管理员荣誉开发者油中2周年生态建设者喜迎中秋油中3周年挑战者 lv2

    发表于 2023-11-9 09:53:09 | 显示全部楼层
    示例与注释不一致
    randomInt(1,9)可以了
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。/ 微信公众号:一之哥哥
    回复

    使用道具 举报

  • TA的每日心情
    难过
    2024-4-24 18:57
  • 签到天数: 13 天

    [LV.3]偶尔看看II

    19

    主题

    27

    回帖

    259

    积分

    荣誉开发者

    积分
    259

    荣誉开发者油中2周年生态建设者喜迎中秋

    发表于 2023-11-9 14:27:29 | 显示全部楼层
    王一之 发表于 2023-11-9 09:53
    示例与注释不一致
    randomInt(1,9)可以了

    改了代码忘记改帖子了(
    (1,8) 化成整数百分位概率不均分不好看(
    回复

    使用道具 举报

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表