微信红包生成算法的实现
一、微信红包算法
要求实现是一个微信红包生成的算法,通过提供一个总金额,以及红包数,要求根据红包总数随机生成金额不等的红包,并输出;要求输出的每个红包金额总和要等于总金额,要求最低金额不能少于0.01元。
微信红包算法代码
public class RedPacket {
/**总金额*/
private String sumMoney;
/**红包总数*/
private int count;
/**随机对象*/
private Random r = new Random();
public RedPacket(String sumMoney, int count) {
super();
this.sumMoney = sumMoney;
this.count = count;
}
public ArrayList<BigDecimal> genPacket() throws RedPacketExcption{
ArrayList<BigDecimal> list = new ArrayList<BigDecimal>();
BigDecimal money = new BigDecimal("0.01");
//获取总金额的最小值
double minMoney = money.multiply(new BigDecimal(count)).doubleValue();
BigDecimal totalMoney = new BigDecimal(sumMoney);
//判断金额的最小值是否超过了总金额
if(minMoney > totalMoney.doubleValue()) {
throw new RedPacketExcption("每个红包不能少于0.01元");
}
if(minMoney == totalMoney.doubleValue()) {
//最低金额正好等于总金额(平均分配)
for (int i = 0; i < count; i++) {
list.add(new BigDecimal("0.01"));
}
return list;
}
double total = 0;
//总金额超过没人最低金额
double[] scales = randomScale();
//合计已经分配多少钱
BigDecimal sends = new BigDecimal(0);
for (int i = 0; i < scales.length - 1; i++) {
//根据笔记计算每个红包应分得的金额
money = totalMoney.multiply(new BigDecimal(scales[i])).setScale(2,BigDecimal.ROUND_HALF_EVEN);
//将金额加入集合
list.add(money);
//累计已经分配的金额总值
sends = sends.add(money);
}
//将剩余的金额装入集合
list.add(totalMoney.subtract(sends));
return list;
}
/**
* 随机生成比例,假设一共5人:
* 0.2,0.1,0.3,0.1,0.3
* @return
*/
public double[] randomScale() {
//累计总的随机值
double total = 0;
//临时数组存储每个红包的随机比例
double[] scales = new double[count];
for (int i = 0; i < count; i++) {
//随机获取一个1-100的整数
int rint = r.nextInt(100) + 1;
scales[i] = rint;
total += rint;
}
//计算比例
for (int i = 0; i < count; i++) {
scales[i] = scales[i] / total;
}
return scales;
}
public static void main(String[] args) throws RedPacketExcption {
ArrayList<BigDecimal> list = new RedPacket("10", 5).genPacket();
for (BigDecimal bd : list) {
System.out.print(bd+"元\t");
}
System.out.println();
}
}
二、设计思路
想要满足算法要求必须解决一下问题
1、每个人分的的红包数概率随机
2、每个人的红包数金额之和必须等于红包总金额数
3、每人最少金额为0.01元
注意可以通过BigDecimal封装金额来解决金额精度丢失问题
首先解决最少金额问题
可以判断总人数最低红包之和是否大于总的红包金额
例如:要将红包分给5个人,这时5个人最低金额总和为5*0.01
此时如果发的红包总金额数小与了这五个人的最低金额总和则
不符合最小金额条件,若是等于则可以每人平均分配0.01
ArrayList<BigDecimal> list = new ArrayList<BigDecimal>();
BigDecimal money = new BigDecimal("0.01");
//获取总金额的最小值
double minMoney = money.multiply(new BigDecimal(count)).doubleValue();
BigDecimal totalMoney = new BigDecimal(sumMoney);
//判断金额的最小值是否超过了总金额
if(minMoney > totalMoney.doubleValue()) {
throw new RedPacketExcption("每个红包不能少于0.01元");
}
if(minMoney == totalMoney.doubleValue()) {
//最低金额正好等于总金额(平均分配)
for (int i = 0; i < count; i++) {
list.add(new BigDecimal("0.01"));
}
return list;
}
再来解决概率平均分配问题以及每个人的红包数金额之和必须等于红包总金额数问题
可以定义一个获取比例的方法用来获取每个红包所占总金额的比例
再用每个红包金额所占比例与总金额想乘得到每个红包的金额数,
先把红包平均分配给前(count - 1)个人,再用总红包金额减去前面所有人金额之和便能得到最后一人的红包金额
/**
* 随机生成比例,假设一共5人:
* 0.2,0.1,0.3,0.1,0.3
* @return
*/
public double[] randomScale() {
//累计总的随机值
double total = 0;
//临时数组存储每个红包的随机比例
double[] scales = new double[count];
for (int i = 0; i < count; i++) {
//随机获取一个1-100的整数
int rint = r.nextInt(100) + 1;
scales[i] = rint;
total += rint;
}
//计算比例
for (int i = 0; i < count; i++) {
scales[i] = scales[i] / total;
}
return scales;
}
double total = 0;
//总金额超过没人最低金额
double[] scales = randomScale();
//合计已经分配多少钱
BigDecimal sends = new BigDecimal(0);
for (int i = 0; i < scales.length - 1; i++) {
//根据笔记计算每个红包应分得的金额
money = totalMoney.multiply(new BigDecimal(scales[i])).setScale(2,BigDecimal.ROUND_HALF_EVEN);
//将金额加入集合
list.add(money);
//累计已经分配的金额总值
sends = sends.add(money);
}
//将剩余的金额装入集合
list.add(totalMoney.subtract(sends));
return list;
三、调试程序
public static void main(String[] args) throws RedPacketExcption {
ArrayList<BigDecimal> list = new RedPacket("10", 5).genPacket();
for (BigDecimal bd : list) {
System.out.print(bd+"元\t");
}
System.out.println();
}
已知出现BUG,因为我格式化的金额为四舍五入保留两位有效数字,当输入较小金额时会通过四舍五入会将金额格式化为0。
四、涉及技术
一、Random类的使用
二、BigDecimal类的使用