低调问一句:有人想知道 JS 红包算法小秘密吗?

251 阅读4分钟

手把手教你写 JS 红包算法,面试、实战一把拿捏!

宝子们!一到过年,微信红包 “咻咻” 发个不停。可你知道这红包金额随机分配的背后,藏着什么神秘代码吗?今天就带大家用 JS 揭开大厂常考的红包算法面纱,学完面试、实战都能派上用场啦~

一、红包算法基础代码,先睹为快!

/**
 * 红包
 * @param {number} total 总金额
 * @param {number} num 红包个数
 * @return {number[]} 随机金额数组
*/
function hongbao(total, num) {
    const arr = [];
    let restAmount = total; // 剩余金额
    let restNum = num; // 剩余个数
    for (let i = 0; i < num - 1; i++) {
        let amount = (Math.random() * (restAmount / restNum * 2)).toFixed(2);
        restAmount -= amount;
        restNum --;
        arr.push(amount);
    }
    arr.push(restAmount.toFixed(2));
    return arr;
}
console.log(hongbao(100, 10));

这段代码就是红包算法的 “雏形” 啦!咱们来逐行拆解:

  1. 首先定义hongbao函数,接收total(总金额)和num(红包个数)两个参数,目标是返回一个包含每个红包金额的数组。
  1. 初始化arr数组用来存红包金额,restAmount记录剩余总金额,restNum记录剩余红包个数。
  1. 进入循环,在倒数第二个红包之前,每次通过Math.random()生成一个随机数,再乘以(restAmount / restNum * 2),最后用toFixed(2)保留两位小数,得到一个红包金额。这里乘以 “人均值的两倍”,是为了让金额有一定随机性,又不会太离谱,是不是很巧妙呀😎?
  1. 每确定一个红包金额,就从剩余金额和个数里减去,再把金额存到数组中。
  1. 循环结束后,把最后剩下的金额作为最后一个红包,也存进数组,最后返回结果~

二、等等!这段代码有 “坑”!

虽然上面的代码看起来能跑,但其实暗藏不少问题,在实际应用中可是要 “翻车” 的!

1. 精度误差:钱算不准啦!

JavaScript 里,浮点数计算有个 “老大难” 问题。比如0.1 + 0.2,结果不是0.3,而是0.30000000000000004!咱们的红包算法里,restAmount -= amount;这样的计算,多来几次,最后总金额可能就对不上了😱。这要是发红包少给用户钱,可就麻烦大了!

2. 随机性 “不靠谱”:有人总拿大红包?

现在的算法,随机分配不够科学。有可能连续几个红包都是大金额,后面的人只能捡 “漏”,导致分配不公平。想象一下,你和朋友抢红包,别人都是几十块,你只有几分钱,是不是想 “摔手机”😤?

3. 边界情况没处理:红包金额能为 0?

代码里没保证每个红包至少有0.01元,极端情况下可能出现金额为0的红包,这显然不符合常理。谁发红包会发个空包呀,太 “抠门” 了!

三、优化升级!打造靠谱红包算法

1. 解决精度问题:换个 “姿势” 计算

把金额单位从 “元” 换成 “分”,所有计算都用整数进行,最后再换算回 “元”。这样就能完美避开浮点数精度问题啦~

2. 优化随机性:让红包分配更公平

限制每个红包的最大和最小金额,比如最小0.01元,最大不超过剩余人均金额的2倍,这样就能让金额分布更均匀,大家抢红包都能开心~

3. 处理边界情况:杜绝 “空包”!

在生成红包金额时,强制每个红包至少0.01元(也就是1分),保证每个红包都 “有料”!

优化后的代码来咯:

function hongbao(total, num) {
    const totalCent = total * 100; // 转换为分
    const minCent = 1; // 最小金额1分
    const result = [];
    for (let i = 0; i < num - 1; i++) {
        // 计算当前红包最大可分配金额
        const maxCent = Math.min(totalCent - minCent * (num - i - 1), (totalCent / (num - i)) * 2);
        let amountCent = Math.floor(Math.random() * (maxCent - minCent + 1)) + minCent;
        result.push(amountCent / 100);
        totalCent -= amountCent;
    }
    result.push(totalCent / 100);
    return result;
}

对比之前的代码,新代码多了单位转换、边界限制,还调整了随机范围,是不是严谨多了?

四、总结

红包算法看似简单,实则暗藏玄机,不仅要考虑技术细节,还得融入产品思维,保证用户体验~下次面试遇到红包算法题,或者自己想做个红包小功能,这些知识就能派上用场啦!要是还有疑问,评论区喊我,一起探讨呀~