一 序
需求背景:常叫外面的同学对于美团或者饿了么的那种在微信群里分享的,第N个红包最大的领红包的营销模式很熟悉了。
我们有类似的需求,其实N是后台可以配置的。
营销规则有: 预算金额,有个范围。
第N个最大,N的范围配置。
最大金额:有个配置范围。
剩余红包金额:配置范围。
qa在测试的时候,发现按照平均数,有除不尽的case。需要重试。
思考了一下,换个思路实现拆红包的第N个最大的实现。
二 一种实现方案:
import java.util.Arrays;
import java.util.Random;
import com.alibaba.fastjson.JSON;
public class RedPackageTest {
//小包
private static final int smalllow =2;
private static final int smallhigh =5;
//预算
private static final int[] yusuan ={30,31,32,33,34,35};
//最大
private static final int[] maxnum = {6,7,8};
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
// TODO Auto-generated method stub
//随机取预算
int tmp= (int)Math.floor(Math.random()*yusuan.length);
int yusuanNum = yusuan[tmp];
//随机取最大
int maxtmp= (int)Math.floor(Math.random()*maxnum.length);
int maxNum = maxnum[maxtmp];
//计算可用剩余金额
int avlMoney = yusuanNum -maxNum;
// 计算红包总数
int totalnum= avlMoney/smallhigh+1;
System.out.println("yusuan:"+yusuanNum+",max= "+maxNum+":num:"+totalnum);
int[] result = divived(avlMoney,totalnum);
System.out.println("yusuan:"+yusuanNum+",max= "+maxNum+",other redpackge reslt:"+JSON.toJSONString(result));
}
public static int[] divived(int money, int n) throws InstantiationException, IllegalAccessException{
if(money < n* smalllow|| money> n*smallhigh ){
System.out.println("超出范围,不能分配");
}
// 创建一个长度等于n的红包数组
int[] array = new int[n];
//第一步 每个红包先塞最基础的边界
Arrays.fill(array,smalllow);
//金额减去已分配金额
money -= n*smalllow;
System.out.println("before first: now left money:"+money+",array:"+JSON.toJSONString(array));
for(int i=0;i<n;i++ ){
int tmp= Random.class.newInstance().nextInt(smallhigh) ;//随机取
int tmpnum = tmp<smalllow?smalllow:tmp;
if( (array[i] + tmpnum)<=smallhigh){
array[i] += tmpnum;
money -= tmpnum;//金额减去
}
System.out.println("fisrt reslut:tmpnum:"+tmpnum+",left money:"+money+",array:"+JSON.toJSONString(array));
}
System.out.println("before sec :left money:"+money+",array:"+JSON.toJSONString(array));
//判断剩余未分配的金额是否大于0,分配剩余金额分散开
int j=0;
while (money > 0){
if( array[j%n]<smallhigh)
{
array[j%n] += 1;
money = money -1;
System.out.println("sec : left money:"+money+",array:"+JSON.toJSONString(array));
}
j++;
}
return array;
}
}
对于业务规则,要求是不超预算,所以,可以一开始从预算范围随机取数,在确定最大数的数值与范围。
重点看看刨除了最大数之后,剩余的金额如何在分配范围内进行拆分。
思路是:先根据最小值进行初始化,再进行一次遍历,每次随机填充,不超过上限,对于一轮过后,有剩余的金额,再按整数进行拆分。
运行效果:
yusuan:30,max= 6:num:5
before first: now left money:14,array:[2,2,2,2,2]
fisrt reslut:tmpnum:3,left money:11,array:[5,2,2,2,2]
fisrt reslut:tmpnum:4,left money:11,array:[5,2,2,2,2]
fisrt reslut:tmpnum:2,left money:9,array:[5,2,4,2,2]
fisrt reslut:tmpnum:2,left money:7,array:[5,2,4,4,2]
fisrt reslut:tmpnum:3,left money:4,array:[5,2,4,4,5]
before sec :left money:4,array:[5,2,4,4,5]
sec : left money:3,array:[5,3,4,4,5]
sec : left money:2,array:[5,3,5,4,5]
sec : left money:1,array:[5,3,5,5,5]
sec : left money:0,array:[5,4,5,5,5]
yusuan:30,max= 6,other redpackge reslt:[5,4,5,5,5]
这个case:预算取了30,最大红包6, 剩余:24的分为5个,为:5、4、5、5、5。 复合预期。
抛砖引玉,肯定不是最优的,看看大家有更好的方案可以贴出来。