用 js 实现抢红包算法

对于抢红包来说最重要的就是随机性算法,如何确保每个人获得金额是趋向于平均的,也就是有平均的概率去随机,而尽量避免两极分化

就比如一般的随机算法,假设有 100 元的红包,有 10 个人去抢,第一个人去抢红包的随机范围是(0, 100),一旦此人抢到足够大的金额比如 90,剩下的 9 个人只能在不断缩小的 10 以下的范围随机,这样的奖金范围会很受限。也就是越前面抢的人优势越大。

接着需要说明抢红包的核心规则:

  1. 每个人抢到的金额总和等于红包金额
  2. 确保每个人得到最小的非零金额数
  3. 要保证随机概率尽量分布均匀,避免存在可以通过技巧性钻空子。

接着我们将使用 js 实现3种抢红包的算法:

方法一:Math.random() 直接随机

js 复制代码
function getRandomMoney(totalMoney, totalPeople) {
    let remainMoney = totalMoney;
    let remainPeople = totalPeople;
    let result = [];

    for (let i = 1; i < totalPeople; i++) {
        let max = remainMoney - remainPeople + 1;
        let money = Math.random() * max;
        money = Math.floor(money * 100) / 100; 
        result.push(money);
        remainMoney -= money;
        remainPeople--;
    }

    result.push(remainMoney); // 最后一个人抢剩下的钱

    return result;
}

let totalMoney = 100;
let totalPeople = 5;
let result = getRandomMoney(totalMoney, totalPeople);
console.log(result);

就像开篇提到的,先抢的优势很大。

对于随机 random() 方法大家很熟悉,但方法返回一个大于等于 0 且小于 1 的伪随机浮点数,其实并不足够随机,在足够大的样本下还是会呈现某种趋势。

方法二:两倍均值法

也就是 money = (0, M/N*2) 剩余红包金额M,剩余人数N。

js 复制代码
function getDoubleAverage(totalMoney, totalPeople) {
    let remainMoney = totalMoney;
    let remainPeople = totalPeople;
    let result = [];

    for (let i = 0; i < totalPeople - 1; i++) {
        let avg = remainMoney / remainPeople * 2;
        let money = Math.random() * avg;
        money = Math.floor(money * 100) / 100; 
        result.push(money);
        remainMoney -= money;
        remainPeople--;
    }

    result.push(remainMoney); // 最后一个人抢剩下的钱

    return result;
}

let totalMoney = 100;
let totalPeople = 5;
let result = getDoubleAverage(totalMoney, totalPeople);
console.log(result);

这种方法等于每次抢红包的最大范围为人均的两倍。但是有个问题,最后一次还是任意的随机。这就意味着最后抢的人收益的风险最高。

方法三:线段切割法

js 复制代码
function getRandomMoney(total, num) {
    if (num === 1) {
        return total;
    }

    let max = total - 0.01 * num;
    let randomArr = [0, total];

    for (let i = 1; i < num; i++) {
        let random = Math.random() * max;
        randomArr.push(random);
    }

    randomArr.sort((a, b) => a - b);

    let result = [];
    for (let j = 0; j < num; j++) {
        let money = (randomArr[j + 1] - randomArr[j]).toFixed(2);
        result.push(parseFloat(money));
    }

    return result;
}

let totalMoney = 100;
let totalPeople = 5;
let moneyList = getRandomMoney(totalMoney, totalPeople);
console.log(moneyList);

线段切割法在抢红包等随机分配场景中被认为比较公平的原因主要是因为:线段切割法会将总金额分割成多个小段,然后随机分配给每个人,确保了每个人获得的金额相对均匀,避免了出现极端不公平的情况。

当然这还存在一种可能分割点可能重复,我们需要重新生成。

相关推荐
东风破_4 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
怕浪猫4 小时前
FDE 最大的浪费不是写出有 bug 的代码,而是漂亮地解决了一个错误的问题
面试·架构·github
东风破_4 小时前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
蒲公英eric5 小时前
从客户端到服务端:DVWA DOM 型 XSS 模块完整漏洞分析教程
前端·web安全·ai·xss·dvwa·ai安全
单线程_015 小时前
从案例分析 Vue3 Tokenizer+Parser 源码三
前端·javascript·vue.js
顶点多余5 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
Setsuna_F_Seiei6 小时前
前端转型 Agent 开发 03 之 Agent Tools - 给 Agent 装上手脚
前端·agent·ai编程
jay神6 小时前
【计算机毕业设计】基于SpringBoot的程序教学辅助系统
java·前端·vue.js·spring boot·后端·毕业设计·课程设计
罗西的思考7 小时前
【Agentic RL / 强化学习框架】Molt 设计解读
人工智能·算法·机器学习
黄敬峰7 小时前
Next.js 全栈实战:数据清洗、ORM 设计与 AI Prompt 工程最佳实践
面试·github