UNPKG

js-red-package

Version:

用 js 实现一个类似微信红包的随机算法

130 lines (112 loc) 4.14 kB
"use strict"; /** * * @author xgqfrms * @license MIT * @copyright xgqfrms * @created 2020-09-16 * @modified * * @description 如何用 js 实现一个类似微信红包的随机算法, 最简单的方法实现微信红包的随机算法 * @difficulty Hard * @complexity O(n) * @augments * @example * @link https://www.cnblogs.com/xgqfrms/p/13689802.html * @link https://www.cnblogs.com/xgqfrms/tag/%E7%BA%A2%E5%8C%85/ * @link https://www.zhihu.com/question/22625187/answer/1478941580 * @solutions * */ const log = console.log; const shuffle = (arr = []) => { let len = arr.length; while (len > 1){ const index = Math.floor(Math.random() * len--); [ arr[len], arr[index], ] = [ arr[index], arr[len], ]; } return arr; } /** 算法需要满足条件: 1. 每个人都可以分到至少 0.01 元; 2. 所有人的分到的红包之和与发出的金额相等,不多不少,刚好分完; 3. 每个人分到金额的概率相等; */ /** 假设,发出一个 100元红包,给 10个人分! 算法实现: 1. 按照人数,生成一个等长的数组,且每一个元素的初始化值为 0.01;✅ 2. 将剩余的金额(100 - 10 * 0.01), 按照微信设计的规则(假如是正态分布)进行分配出 10 份; ❓ 3. 将分配好的红包,依次加入到生成的数组中;✅ 4. 最后使用 shuffle 算法打乱数组,并返回; ✅ 5. 将计算好的数组,按照抢红包的顺序作为索引值依次取出红包即可.✅ */ const autoRandomRedPackage = (money, num, limit = 0.01) => { if((money / num) < limit) { log(`💩 请重新输入红包数量! 减少红包数量,或增加红包金额!`); log(`❌ 你输入的红包数量太多了,每个人至少要能分到 0.01 元!`); return false; } else { const originMoney = money; const originLimit = limit; // 精度损失解决方案, 扩大后,再还原 ✅🚀 let multi = 100 * (100 / money); money *= multi; limit *= multi; const result = [...new Uint8Array(num)].fill(limit, 0, num); // 1. 将剩余的红包,均分✅,如果有余数,随机的添加到数组的一个元素上 const restLimit = (money - limit * num) / limit; const reminderLimit = (restLimit % num); const reminderMoney = reminderLimit * limit; const averageLimit = (restLimit - reminderLimit) / num; for (let i = 0; i < num; i++) { const index = parseInt(Math.random() * averageLimit); const randomMoney = index * limit; const leftMoney = (averageLimit - index) * limit; // 2. 在平均后的范围内,计算出一个随机数,将分配好的红包,依次加入到生成的数组中;✅ result[i] += randomMoney; // 3. 分配后剩余的红包,随机加入到生成的数组中;✅ const j = parseInt(Math.random() * num); result[j] += leftMoney; } // 4. 将平均后的余数红包,随机加入到生成的数组中;✅ if(reminderMoney > 0) { const index = parseInt(Math.random() * num); result[index] += reminderMoney; } const temp = shuffle(result).map(i => i / multi); const total = temp.reduce((acc, i) => acc += i*multi, 0) / multi; if(total !== originMoney) { return autoRandomRedPackage(originMoney, num, originLimit); } const [min] = [...temp].sort((a, b) => a - b > 0 ? 1 : -1); const [max] = [...temp].sort((a, b) => a - b > 0 ? -1 : 1); return { total: total, result: temp, desc: ` 🕵️‍♂️ 你输入的红包总额是 ${originMoney} 元, 红包数量是 ${num} 个! 👍 最大的红包是 ${max} 元! 👎 最小的红包是 ${min} 元! `, }; } } // module.exports.autoRandomRedPackage = autoRandomRedPackage; // module.exports = { // autoRandomRedPackage, // }; // default,覆盖上面的所有 exports (方法复写/重载) module.exports = autoRandomRedPackage; /* // const autoRandomRedPackage = require("js-red-package"); const autoRandomRedPackage = require("./index.min.js"); TypeError: autoRandomRedPackage is not a function */