你们在抢红包,而程序员在研究红包算法
摘要:近期,有需求需要我写一个红包算法,要求幅度不要太偏锋,浮动尽量不要太大,限制最大值,于是,我开始了~
你们在抢红包,而程序员在研究红包算法。
我就先试着写一下吧~也不知道对不对,看了网上的说法,好像到现在为止官方也没有给出一个确切的算法,只好在这里献丑了,先贴出代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
/** * create by :XJH * description :红包算法 * create time :2017/12/4/004 * * @param money 红包总金额 * @param num 红包数量 * @param max 每个红包最大金额(建议max = money / num * 2)(如果max过于庞大,造成极端的可能性非常大) * @return java.util.List<java.lang.Double> */ public static List<Double> getMoney(double money, int num, float max) { if ((money / num) < 0.01) { //如果红包按照每个人0.01都不够分配,则返回null return null; } if (0.01 == (money / num)) { //如果这个红包分配下来每个人都是0.01,直接返回 double baseDouble[] = new double[num]; Arrays.fill(baseDouble, 0.01d); List<Double> baseList = new ArrayList<>(num); for (double d : baseDouble) { baseList.add(d); } return baseList; } if (num * max < money) { //每个人最大值都不能达到满额,则返回null return null; } Random r = new Random(); DecimalFormat format = new DecimalFormat(".##"); double[] dou = new double[num]; double redMoney = 0; double nextMoney = money; double sum = 0; int index = 0; for (int i = num; i > 0; i--) { if (i == 1) { dou[index] = nextMoney; } else { while (true) { String str = format.format(r.nextDouble() * nextMoney); redMoney = Double.parseDouble(str); if (redMoney > max || max * (i - 1) < (money - sum - redMoney) || (money - sum - redMoney) < (i - 1) * 0.01) { //如果本次红包大于max则重来 //如果本次红包结束,且接下来每个人都是最大值之和小于剩余的钱(也就是人都抢完红包了,还有剩余的钱)则重来 //如果本次红包结束,且接下来每个人都是最小值之和大于剩余的钱(也就是人都抢完红包了,钱不够)则重来 continue; } if (redMoney > 0 && redMoney <= nextMoney) { //本次红包必须大于0 //本次红包必须小于或等于nextMoney break; } } nextMoney = Double.parseDouble(format.format(nextMoney - redMoney)); sum = sum + redMoney; dou[index] = redMoney; index++; } } //数组转换list,随机打乱顺序 List<Double> list = new ArrayList<>(); for (double d : dou) { list.add(d); } Collections.shuffle(list); return list; } |
运行结果:
红包总量:10,红包个数:10,最大值:2:0.61、1.19、0.71、1.9、1.57、0.47、0.4、1.17、0.45、1.53
红包总量:10,红包个数:10,最大值:2:2.0:、0.77、0.57、1.93、1.94、0.94、1.2、0.17、0.05、0.43
红包总量:1,红包个数:10,最大值:2:0.37、0.52、0.01、0.01、0.01、0.01、0.02、0.01、0.01、0.03
红包总量:0.1,红包个数:10,最大值:2:0.01、0.01、0.01、0.01、0.01、0.01、0.01、0.01、0.01、0.01
感觉这份有点low,而且不是很稳定,在max过大时红包的量过于极端,于是我又开始了~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
package com.gysdlmy.euroluce.utils; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; /** * @ Author :XJH. * @ Date :Created in 16:35 2017/12/5/005. * @ Description: * 随机产生红包:金额正太分布 * <p> * 如果非标准正态分布X~N(μ,σ^2),那么关于X的一个一次函数 (X-μ)/σ ,就一定是服从标准正态分布N(0,1)。 * 举个具体的例子,一个量X,是非标准正态分布,期望是10,方差是5^2(即X~N(10,5^2));那么对于X的线性函数Y=(X-10)/5,Y就是服从标准正态分布的Y~N(0,1)。 * @ Modified By: */ public class MainDemo { private static Random random = new Random(); private static BigDecimal MIN_VALUE = new BigDecimal("0.01"); private static boolean isMin = false; /** * create by :XJH * description : * create time :2017/12/5/005 * * @param amountValue 红包总金额 * @param sizeValue 红包个数 * @param maxMutValue 剩余红包限定倍数 * @param sigmaValue 标准差倍数 * @return java.util.List<java.math.BigDecimal> */ public static List<BigDecimal> getAllHotPacket(double amountValue, double sizeValue, double maxMutValue, double sigmaValue) { //红包总金额 BigDecimal restAmount = new BigDecimal(String.valueOf(amountValue)); //红包总金额 BigDecimal size = new BigDecimal(String.valueOf(sizeValue)); //红包个数 BigDecimal mu = restAmount.divide(size, 2, BigDecimal.ROUND_HALF_DOWN); //(平均值)amountValue/sizeValue保留两位小数四舍五入 BigDecimal avg = new BigDecimal(mu.toString()); //平均值 BigDecimal MAX_MUT = new BigDecimal(String.valueOf(maxMutValue)); double sigma = sigmaValue <= 0 ? 1 : sigmaValue; List<BigDecimal> hotPacketPool; //返回的红包集合 do { restAmount = new BigDecimal(String.valueOf(amountValue)); //这是为了最后的数据不正确重来做准备 hotPacketPool = new ArrayList<>(size.intValue()); int hotPacketSize = size.intValue() - 1; //随机出前size-1个红包,最后一个红包取剩余值,并且最后一个红包不能过大,有均值的限定倍数 for (int i = 0; i < hotPacketSize; i++) { BigDecimal randomBigDecimal = getRandomHotPacketAmount(mu.doubleValue(), sigma, restAmount, size.intValue() - 1); restAmount = restAmount.subtract(randomBigDecimal); //总金额减去本次红包的金额 //System.out.println("剩下的红包金额:" + restAmount); size = size.subtract(BigDecimal.ONE); //剩余红包个数-1 mu = restAmount.divide(size, 2, BigDecimal.ROUND_HALF_DOWN); //(平均值)剩余金额/剩余红包个数,保留两位小数 hotPacketPool.add(randomBigDecimal); //本次红包放入返回的集合中 } hotPacketPool.add(restAmount); //最后一个红包剩余的金额放到集合中 size = new BigDecimal(String.valueOf(sizeValue)); //这是为了最后的数据不正确重来做准备 } while (restAmount.compareTo(avg.multiply(MAX_MUT)) > 0); //打乱红包顺序,因为越早的红包均值最高 //倒序遍历list,然后在当前位置随机一个比当前位置小的int数字,交换数字 Collections.shuffle(hotPacketPool); return hotPacketPool; } /** * create by :XJH * description :根据剩余红包金额均值,标准差大小,计算出随机红包的大小 * create time :2017/12/5/005 * * @param mu 平均值 * @param sigma 标准差倍数 * @param rest 剩下的钱 * @param restSize 还剩多少红包 * @return java.math.BigDecimal */ private static BigDecimal getRandomHotPacketAmount(double mu, double sigma, BigDecimal rest, int restSize) { if (isMin) { return MIN_VALUE; } BigDecimal radomNo; //剩余最小的钱(剩下的人(本次除外) * 0.01) BigDecimal minRest = MIN_VALUE.multiply(new BigDecimal(restSize)); //随机出的红包也得满足剩余红包最少0.01 do { radomNo = getRandom(mu, mu * sigma); //随机本次红包 } while (rest.subtract(radomNo).subtract(minRest).compareTo(BigDecimal.ZERO) < 0); //如果剩余的钱-本次红包-剩下的人(本次除外)*0.01<0,则重来 if (rest.subtract(radomNo).subtract(minRest).compareTo(BigDecimal.ZERO) == 0) { //如果本次红包随机完毕,符合要求,且剩下的钱仅仅够分接下来的每一个人0.01元,则在接下来几次直接返回0.01。 isMin = true; } BigDecimal randomBigDecimal = radomNo; //对红包金额取2位小数 randomBigDecimal = randomBigDecimal.setScale(2, BigDecimal.ROUND_HALF_DOWN); //判断金额不能小于0.01元 randomBigDecimal = randomBigDecimal.compareTo(MIN_VALUE) > 0 ? randomBigDecimal : MIN_VALUE; return randomBigDecimal; } /** * create by :XJH * description :产生mu sigma的正态分布的double值 * create time :2017/12/5/005 * * @param mu 剩余金额/剩余红包(平均值) * @param sigma 标准差倍数 * @return java.math.BigDecimal */ private static BigDecimal getRandom(double mu, double sigma) { double randomValue = random.nextGaussian() * sigma + mu; BigDecimal value = new BigDecimal(String.valueOf(randomValue)).abs(); return value; } public static void main(String[] args) { BigDecimal all = BigDecimal.ZERO; List<BigDecimal> allHotPacket = getAllHotPacket(100d, 10d, 3d, 1d); int size = allHotPacket.size(); BigDecimal max = BigDecimal.ZERO; int maxIndex = 0; for (int i = 0; i < size; i++) { BigDecimal amout = allHotPacket.get(i); System.out.println("第" + (i + 1) + "随机的红包金额大小:" + amout); if (amout.compareTo(max) > 0) { max = amout; maxIndex = i + 1; } all = all.add(amout); } System.out.println("所有红包金额为红包:" + all); System.out.println("手气最佳为:第" + maxIndex + "个红包,金额为:" + max); } } |
运行结果:
第1随机的红包金额大小:3.95
第2随机的红包金额大小:27.20
第3随机的红包金额大小:8.85
第4随机的红包金额大小:5.91
第5随机的红包金额大小:9.41
第6随机的红包金额大小:28.66
第7随机的红包金额大小:4.09
第8随机的红包金额大小:1.89
第9随机的红包金额大小:2.09
第10随机的红包金额大小:7.95
所有红包金额为红包:100.00
手气最佳为:第6个红包,金额为:28.66
第1随机的红包金额大小:14.29
第2随机的红包金额大小:3.13
第3随机的红包金额大小:6.04
第4随机的红包金额大小:12.51
第5随机的红包金额大小:15.44
第6随机的红包金额大小:11.40
第7随机的红包金额大小:15.22
第8随机的红包金额大小:8.10
第9随机的红包金额大小:7.30
第10随机的红包金额大小:6.57
所有红包金额为红包:100.00
手气最佳为:第5个红包,金额为:15.44
第1随机的红包金额大小:1.12
第2随机的红包金额大小:0.08
第3随机的红包金额大小:2.61
第4随机的红包金额大小:1.80
第5随机的红包金额大小:1.13
第6随机的红包金额大小:0.41
第7随机的红包金额大小:0.08
第8随机的红包金额大小:2.14
第9随机的红包金额大小:0.23
第10随机的红包金额大小:0.40
所有红包金额为红包:10.00
手气最佳为:第3个红包,金额为:2.61
硬性需求,结果还算满意,你们觉得呢~
你们在抢红包,而程序员在研究红包算法~
以诚感人者,人亦诚而应。
铁西余文乐
老铁,可以
夕凉
“你们在抢红包,而我在找程序员开后门~”