在网上搜索一番后找到了一个微信正统(据说)算法 Java 版本的代码,下面对这个算法进行一下分析:
public static double getRandomMoney(LeftMoneyPackage _leftMoneyPackage) {
// remainSize 剩余的红包数量
// remainMoney 剩余的钱
if (_leftMoneyPackage.remainSize == 1) {
_leftMoneyPackage.remainSize--;
return (double) Math.round(_leftMoneyPackage.remainMoney * 100) / 100;
}
Random r = new Random();
double min = 0.01; //
double max = _leftMoneyPackage.remainMoney / _leftMoneyPackage.remainSize * 2;
double money = r.nextDouble() * max;
money = money <= min ? 0.01: money;
money = Math.floor(money * 100) / 100;
_leftMoneyPackage.remainSize--;
_leftMoneyPackage.remainMoney -= money;
return money;
}
可以看出核心流程是通过生成一个范围为 0.01 - 平均值*2 的随机数,来作为红包的大小,下面通过 JS 来实现一下:
// 这里引用 math.js 数学库用于处理精度问题
import * as math from 'mathjs';
// 封装高精度的加法
function add(num1: number, num2: number) {
return Number(
math.chain(math.bignumber(num1)).add(math.bignumber(num2)).done()
);
}
// 封装高精度的减法
function substract(num1: number, num2: number) {
return Number(
math.chain(math.bignumber(num1)).subtract(math.bignumber(num2)).done()
);
}
// 声明一个红包的类
class LeftMoneyPackage {
remainSize: number;
remainMoney: number;
constructor(remainSize, remainMoney) {
this.remainSize = remainSize;
this.remainMoney = remainMoney;
}
}
// 获取随机金额的函数
function getRandomMoney(leftMoneyPackage: LeftMoneyPackage): number {
let { remainSize, remainMoney } = leftMoneyPackage;
if (remainSize === 1) {
return remainMoney;
}
const min = 0.01;
// 获取最大值
const max = (remainMoney / remainSize) * 2;
// 在 0 - max 之间取值,这里进行处理获取一个两位小数
let money = Math.floor(Math.random() * max * 100) / 100;
money = money < min ? 0.01 : money;
leftMoneyPackage.remainSize--;
leftMoneyPackage.remainMoney = substract(remainMoney, money);
return money;
}
const leftMoneyPackage = new LeftMoneyPackage(10, 10);
let num = 0;
for (let i = 0; i < 10; i++) {
const result = getRandomMoney(leftMoneyPackage);
num = add(num, result);
console.log(result);
}
console.log(num);
可以看出,该算法本身的逻辑还是比较好理解,唯一需要注意的就是 JS 浮点数运算的精度问题.