算法介绍
一、红包金额限制
对于微信红包,我们知道每人随机的最小红包是1分,最大金额是200元,这里我们同样来设置红包的范围,下面代码我们统一金钱单位为分。
/**
* 1.总金额不超过200*100 单位是分
* 2.每个红包都要有钱,最低不能低于1分,最大金额不能超过200*100
*/
private static final int MINMONEY = 1;
private static final int MAXMONEY = 200*100;
二、判断红包金额是否合法
在分配红包前判断其是否合法
/**
* 红包合法性校验
* @param money
* @param count
* @return
*/
private boolean isRight(int money,int count){
double avg = money/count;
//小于最小金额
if (avg<MINMONEY) {
return false;
}else if (avg>MAXMONEY) {
return false;
}
return true;
}
三、随机产生一个红包
在随机产生红包的时候,我们知道随机一个红包后,剩余的红包个数,这时我们又知道红包的最大值、最小值、那么我们就知道了剩余金额需要在一个范围内,因此也知道了这个产生的随机红包的最值,所以每次产生下个红包只需一次随机即可。
举个简单的例子,加入现在有一个5分4个红包,每个红包要求至少1分,最大20000分,那么分出第一个红包后,剩余3个红包的钱数要在1*3=3(分),20000*3=60000(分)之间,因此也就是说,分配第一个红包的随机金额在5-60000=59995(分)和5-3=2(分)之间,又由于我们对红包的金额要求在1~20000之间,因此确定第一次随机金额的范围是【1分,2分】之间。
/**
* 随机分配一个红包
* @param mnoney
* @param minS:最小金额
* @param maxS:最大金额
* @param count
* @return
*/
private int randomRedPacket(int money,int minS,int maxS,int count){
//若只有一个,直接返回红包
if (count==1) {
return money;
}
//如果最大金额和最小金额相等,直接返回金额
if (minS==maxS) {
return minS;
}
int max=maxS>money?money:maxS;
//分配红包正确情况,允许红包的最大值
int maxY = money-(count-1)*minS;
//分配红包正确情况,允许红包最小值
int minY = money-(count-1)*maxS;
//随机产生红包的最小值
int min = minS>minY?minS:minY;
//随机产生红包的最大值
max = max>maxY?maxY:max;
//随机产生一个红包
return (int)Math.rint(Math.random()*(max-min) +min);
}
四、实现红包的分配
这里为了避免某一个红包占用大量资金,我们需要设定非最后一个红包的最大金额,我们把他设置成红包金额平均值的N倍;有了以上方法,就能实现红包分配了
/**
* 拆分红包
* @param money 红包金额
* @param count 个数
* @return
*/
public List<Integer> splitRedPacket(int money,int count){
//红包合法性分析
if (!isRight(money, count)) {
return null;
}
//红包列表
List<Integer> list = new ArrayList<>();
//每个红包的最大的金额为平均金额的TIMES倍
int max = (int)(money*TIMES/count);
max = max>MAXMONEY?MAXMONEY:max;
//分配红包
for(int i=0;i<count;i++){
int one = randomRedPacket(money, MINMONEY, max, count-i);
list.add(one);
money-=one;
}
return list;
}
完整代码
import java.util.ArrayList;
import java.util.List;
/**
*
* @author zy
* @date 2017年10月13日 上午9:57:07
* @Decription Java仿实现微信红包分配算法
*/
public class RedPacket {
/**
* 1.总金额不超过200*100 单位是分
* 2.每个红包都要有钱,最低不能低于1分,最大金额不能超过200*100
*/
private static final int MINMONEY = 1;
private static final int MAXMONEY = 200*100;
/**
* 这里为了避免某一个红包占用大量资金,我们需要设定非最后一个红包的最大金额,
* 我们把他设置为红包金额平均值的N倍
*/
private static final double TIMES = 3.1;
/**
* 红包合法性校验
* @param money
* @param count
* @return
*/
private boolean isRight(int money,int count){
double avg = money/count;
//小于最小金额
if (avg<MINMONEY) {
return false;
}else if (avg>MAXMONEY) {
return false;
}
return true;
}
/**
* 随机分配一个红包
* @param mnoney
* @param minS:最小金额
* @param maxS:最大金额
* @param count
* @return
*/
private int randomRedPacket(int money,int minS,int maxS,int count){
//若只有一个,直接返回红包
if (count==1) {
return money;
}
//如果最大金额和最小金额相等,直接返回金额
if (minS==maxS) {
return minS;
}
int max=maxS>money?money:maxS;
//分配红包正确情况,允许红包的最大值
int maxY = money-(count-1)*minS;
//分配红包正确情况,允许红包最小值
int minY = money-(count-1)*maxS;
//随机产生红包的最小值
int min = minS>minY?minS:minY;
//随机产生红包的最大值
max = max>maxY?maxY:max;
//随机产生一个红包
return (int)Math.rint(Math.random()*(max-min) +min);
}
/**
* 拆分红包
* @param money 红包金额
* @param count 个数
* @return
*/
public List<Integer> splitRedPacket(int money,int count){
//红包合法性分析
if (!isRight(money, count)) {
return null;
}
//红包列表
List<Integer> list = new ArrayList<>();
//每个红包的最大的金额为平均金额的TIMES倍
int max = (int)(money*TIMES/count);
max = max>MAXMONEY?MAXMONEY:max;
//分配红包
for(int i=0;i<count;i++){
int one = randomRedPacket(money, MINMONEY, max, count-i);
list.add(one);
money-=one;
}
return list;
}
public static void main(String[] args) {
RedPacket redPacket = new RedPacket();
System.out.println(redPacket.splitRedPacket(20000, 100));
}
}