红包算法,至少需要3个参数
- 红包总金额
- 红包个数
- 红包最小值
通常来说,红包最小值是0.01。
首先思考这种算法:给每个红包分配最小值,然后从头开始依次从剩余金额中随机抽取金额加入到已分配最小值的红包中,把最后的金额加入到最后一个红包。
实际上,上述算法是有问题的,如果分配的红包比较多,就能发现第一个分配概率是(0,可分配金额总数)而后续的一个是(0,前一个分配剩余金额),造成了第一个特别巨大,剩余的越来越小。
我们发现不得不思考一个问题:一个红包最大值应该是多少?先附代码
public static void main(String[] args) {
double total = 100; // 金钱总数
int number = 4; // 红包总数
double min = 0.01; // 假设最小金额,正整数,或0.1或0.xx1等可被1整除的小数
if(number*min>total){
throw new IllegalArgumentException("钱不够,请减少金额或缩小最小值");
}
// 如果最小金额是小数,算出需扩大倍数,否则倍数为1
int beiShu;
if(min>1){
beiShu = 1;
}else{
beiShu = (int) (1/min);
}
// 扩大后的总数、扩大后的最小值、扩大后的每个红包金额数
int t_total = (int) (total*beiShu);
int t_min = (int) (min*beiShu);
int[] thb = new int[number];
Arrays.fill(thb,t_min); // 填充最小值
// 可分配金额
t_total-= (number*t_min);
Random random = new Random();
int big = random.nextInt(number); // 4倍暴击概率
for (int i = 0; i < thb.length-1; i++) { // 随机分配前n-1个红包
if(t_total==0){ // 已全部分配
break;
}
int rd;
int t_num = number-i; // 当前剩余需分配红包个数
if(i==big&&t_num>=4){
// 暴击最高金额:(可分配总金额/剩余需分配红包个数)*4
rd = random.nextInt(t_total/t_num*4);
}else{
// 最高金额:(可分配总金额/剩余需分配红包个数)*2
rd = random.nextInt(t_total/t_num*2);
}
t_total-=rd;
thb[i]+=rd;
}
thb[number-1]+=t_total; // 最后剩余金额给最后一个红包
// 最终缩小回原始倍数后的每个红包数
double[] hb = new double[number];
for (int i = 0; i < number; i++) {
hb[i] = thb[i]/(beiShu*1.0);
}
// 最终红包
System.out.println("总金额:"+total+",红包数:"+number+",最小值不少于"+min+",各个红包金额如下:");
System.out.println(Arrays.toString(hb));
// 结果校验、以及计算运气王
int ttt = 0;
double max = hb[0];
for (int i = 0; i < thb.length; i++) {
ttt += thb[i];
if(hb[i]>max){
max = hb[i];
}
}
System.out.println("运气王:"+max);
String check = (ttt==total*beiShu)?"通过":"不通过";
System.out.println("红包校验结果:"+check);
System.out.println(Arrays.toString(thb)+"="+ttt);
}
如果计算平均值,应该是(可分配金额/需分配个数),那么我们把随机数从0到平均数的2倍。这样的分布是比较平均的,为了增加随机性,增加一个4倍暴击概率,从0-n中随机取一个值,一旦遍历到它且当前需分配大于等于4,可触发该4倍暴击概率。
需要注意,java中的float,double,需要转化为整数计算,否则很难得到正确的小数点。因为java的float、double计算时可能精度混乱。
本篇完,还有疑问?留下评论吧