如何用 js 实现一个类似微信红包的随机算法
js, 微信红包, 随机算法
/** * * @author xgqfrms * @license MIT * @copyright xgqfrms * @created -09-16 * @modified * * @description 如何用 js 实现一个类似微信红包的随机算法, 最简单的方法实现微信红包的随机算法 * @difficulty Hard * @complexity O(n) * @augments * @example * @link /xgqfrms/p/13689802.html * @link /xgqfrms/tag/%E7%BA%A2%E5%8C%85/ * @link /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) { // alert log(`? 请重新输入红包数量! 减少红包数量,或增加红包金额!`); log(`❌ 你输入的红包数量太多了,每个人至少要能分到 0.01 元!`); return false; } else { const originMoney = money; const originLimit = limit; let multi = 100 * (100 / money); money *= multi; limit *= multi; // log(`multi =`, 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); // log(`temp =`, temp); const total = temp.reduce((acc, i) => acc += i*multi, 0) / multi; // log(`total !== originMoney`, total !== originMoney, total, originMoney); if(total !== originMoney) { return autoRandomRedPackage(originMoney, num, originLimit); } const [min, ...rest1] = temp.sort((a, b) => a - b > 0 ? 1 : -1); const [max, ...rest2] = temp.sort((a, b) => a - b > 0 ? -1 : 1); return { total: total, result: temp, desc: ` ?️♂️ 你输入的红包总额是 ${originMoney} 元, 红包数量是 ${num} 个! ? 最大的红包是 ${max} 元! ? 最小的红包是 ${min} 元! `, // desc: ` // ?️♂️ 你输入的红包总额是 ${originMoney} 元, 红包数量是 ${num} 个!\n // ? 最大的红包是 ${max} 元!\n // ? 最小的红包是 ${min} 元!\n // `, }; // return temp; } } const total = arr => arr.result.reduce((acc, i) => acc += i*100, 0) / 100; // 测试 const test = autoRandomRedPackage(0.1, 11);// ❌ 异常处理 const test0 = autoRandomRedPackage(0.1, 5); const test1 = autoRandomRedPackage(0.1, 10); // ✅ ok const test2 = autoRandomRedPackage(1, 10); const test3 = autoRandomRedPackage(10, 10); const test4 = autoRandomRedPackage(100, 10); const test5 = autoRandomRedPackage(100, 11); log(`\ntest =`, test); log(`total =`, test && total(test)); log(`\ntest =`, test0); log(`total =`, test0 && total(test0)); log(`\ntest =`, test1); log(`total =`, test1 && total(test1)); log(`\ntest =`, test2); log(`total =`, test2 && total(test2)); log(`\ntest =`, test3); log(`total =`, test3 && total(test3)); log(`\ntest =`, test4); log(`total =`, test4 && total(test4)); log(`\ntest =`, test5); log(`total =`, test5 && total(test5)); /* $ node node perfect-solution.js ? 请重新输入红包数量! 减少红包数量,或增加红包金额! ❌ 你输入的红包数量太多了,每个人至少要能分到 0.01 元! test = false total = false test = { total: 0.1, result: [ 0.03, 0.03, 0.02, 0.01, 0.01 ], desc: '\n' + ' ?️♂️ 你输入的红包总额是 0.1 元, 红包数量是 5 个!\n' + ' ? 最大的红包是 0.03 元!\n' + ' ? 最小的红包是 0.01 元!\n' + '' } total = 0.1 test = { total: 0.1, result: [ 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 ], desc: '\n' + ' ?️♂️ 你输入的红包总额是 0.1 元, 红包数量是 10 个!\n' + ' ? 最大的红包是 0.01 元!\n' + ' ? 最小的红包是 0.01 元!\n' + '' } total = 0.1 test = { total: 1, result: [ 0.16, 0.13, 0.13, 0.12, 0.09, 0.08, 0.08, 0.08, 0.07, 0.06 ], desc: '\n' + ' ?️♂️ 你输入的红包总额是 1 元, 红包数量是 10 个!\n' + ' ? 最大的红包是 0.16 元!\n' + ' ? 最小的红包是 0.06 元!\n' + '' } total = 1 test = { total: 10, result: [ 2.43, 1.73, 1.43, 1.36, 1, 0.78, 0.66, 0.28, 0.25, 0.08 ], desc: '\n' + ' ?️♂️ 你输入的红包总额是 10 元, 红包数量是 10 个!\n' + ' ? 最大的红包是 2.43 元!\n' + ' ? 最小的红包是 0.08 元!\n' + '' } total = 10 test = { total: 100, result: [ 23.71, 16.9, 13.24, 12, 10, 8.6, 8.33, 3.6, 1.92, 1.7 ], desc: '\n' + ' ?️♂️ 你输入的红包总额是 100 元, 红包数量是 10 个!\n' + ' ? 最大的红包是 23.71 元!\n' + ' ? 最小的红包是 1.7 元!\n' + '' } total = 100 test = { total: 100, result: [ 17.59, 11.68, 11.55, 10.2, 8.83, 8.81, 8.24, 7.55, 7.44, 6.66, 1.45 ], desc: '\n' + ' ?️♂️ 你输入的红包总额是 100 元, 红包数量是 11 个!\n' + ' ? 最大的红包是 17.59 元!\n' + ' ? 最小的红包是 1.45 元!\n' + '' } total = 100 */"use strict";
refs
/xgqfrms/tag/红包/
©xgqfrms -
发布文章使用:只允许注册用户才可以访问!
xgqfrms
标签:微信,随机算法,红包,js,微信红包