红包算法思路
随机,额度在0.01和(剩余平均值*2)之间。
例如:发100块钱,总共10个红包,那么平均值是10块钱一个,那么发出来的红包的额度在0.01元~20元之间波动。
当前面3个红包总共被领了40块钱时,剩下60块钱,总共7个红包,那么这7个红包的额度在:0.01~((60/7) * 2)=17.14之间。
注意:这里的算法是每被抢一个后,剩下的会再次执行上面的这样的算法
这样算下去,会超过最开始的全部金额,因此到了最后面如果不够这么算,那么会采取如下算法:保证剩余用户能拿到最低1分钱即可。
如果前面的人手气不好,那么后面的余额越多,红包额度也就越多,因此实际概率一样的。
package arthur.dy.lee.red.envelope;
public class RedPackage {
public int remainSize;
public double remainMoney;//生产用BigDecimal
public RedPackage(int remainSize, double remainMoney){
this.remainSize = remainSize;
this.remainMoney = remainMoney;
}
}
package arthur.dy.lee.red.envelope;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.DecimalFormat;
/**
* 微信红包算法
* <p>
* 随机,额度在0.01和(剩余平均值*2)之间。
* 例如:发100块钱,总共10个红包,那么平均值是10块钱一个,那么发出来的红包的额度在0.01元~20元之间波动。
* 当前面3个红包总共被领了40块钱时,剩下60块钱,总共7个红包,那么这7个红包的额度在:0.01~(60/7*2)=17.14之间。
* 注意:这里的算法是每被抢一个后,剩下的会再次执行上面的这样的算法
* 这样算下去,会超过最开始的全部金额,因此到了最后面如果不够这么算,那么会采取如下算法:保证剩余用户能拿到最低1分钱即可。
* 如果前面的人手气不好,那么后面的余额越多,红包额度也就越多,因此实际概率一样的。
*/
public class RedEnvelope {
public static double getRandomMoney(RedPackage redPackage) {
// remainSize 剩余的红包数量
// remainMoney 剩余的钱
if (redPackage.remainSize == 1) {
redPackage.remainSize--;
return (double) Math.floor(redPackage.remainMoney * 100) / 100;
}
double min = 0.01;
double max = (redPackage.remainMoney / redPackage.remainSize) * 2;
SecureRandom random = new SecureRandom();
double money = random.nextDouble() * max;
if (money < min) {
money = min;
}
money = (double) Math.floor(money * 100) / 100;
redPackage.remainSize--;
redPackage.remainMoney = redPackage.remainMoney - money;
return money;
}
public static void main(String[] args) {
DecimalFormat df = new DecimalFormat("#.00");
for (int i = 0; i < 20; i++) {
RedPackage redPackage = new RedPackage(10, 100);
BigDecimal count = new BigDecimal(0);
while (redPackage.remainSize != 0) {
double money = RedEnvelope.getRandomMoney(redPackage);
System.out.print(money + " ");
count = count.add(new BigDecimal(money));
}
System.out.println(" |totalCount=" + df.format(count));
System.out.println("-------");
}
}
}
运行10次结果
13.75 2.82 19.18 3.0 7.62 4.2 1.61 20.74 26.92 0.16 |totalCount=100.00
12.66 1.1 17.01 5.73 4.58 3.74 18.56 8.94 18.55 9.13 |totalCount=100.00
2.43 20.83 1.1 14.4 16.31 15.9 11.42 5.83 0.09 11.69 |totalCount=100.00
12.91 15.4 16.72 6.47 13.14 9.52 3.9 9.59 5.85 6.5 |totalCount=100.00
10.07 0.1 3.62 2.33 7.61 27.12 9.34 22.12 6.39 11.3 |totalCount=100.00
12.22 6.61 6.78 0.68 2.29 0.24 8.46 1.62 35.79 25.3 |totalCount=99.99
0.68 5.4 6.5 4.44 14.24 0.64 0.07 0.03 10.15 57.85 |totalCount=100.00
10.79 2.08 7.15 17.5 0.52 6.39 6.01 2.65 0.54 46.37 |totalCount=100.00
19.98 6.85 12.3 12.15 12.12 1.47 7.72 4.53 18.53 4.35 |totalCount=100.00
0.64 4.46 5.48 0.03 18.84 1.85 24.59 14.22 21.98 7.91 |totalCount=100.00
带范围的红包算法思路
思路:
- 1、剩余金额=总值-抽奖次数*最小值
- 2、剩余金额和次数当作红包算法,计算返回抽奖值,最后再加上最小值即可。
package arthur.dy.lee.red.envelope;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.DecimalFormat;
/**
* 带范围的红包,最小值6和最大值为12
*
* 思路:
* 1、剩余金额=总值-抽奖次数*最小值
* 2、剩余金额和次数当作红包算法,计算返回抽奖值,最后再加上最小值即可。
*/
public class RangeRedEnvelope {
public static double minRange = 6; //红包最大值
public static double maxRange = 12; //红包最大值
public static DecimalFormat df = new DecimalFormat("#.00");
public static void getWrappedRandomMoney(RedPackage redPackage) {
//加上范围后,不同点1
redPackage.remainMoney = redPackage.remainMoney - redPackage.remainSize * minRange;
double money;
BigDecimal count = new BigDecimal(0);
while (redPackage.remainSize >0) {
money = (double) Math.floor((getRandomMoney(redPackage) + minRange) * 100) / 100;
System.out.print(money + " ");
count = count.add(new BigDecimal(money));
}
System.out.println(" |totalCount=" + df.format(count));
}
public static double getRandomMoney(RedPackage redPackage) {
// remainSize 剩余的红包数量
// remainMoney 剩余的钱
if (redPackage.remainSize == 1) {
redPackage.remainSize--;
return (double) Math.floor(redPackage.remainMoney * 100) / 100;
}
double min = 0.01;
double max = (redPackage.remainMoney / redPackage.remainSize) * 2;
SecureRandom random = new SecureRandom();
double money;
if (max > (maxRange - minRange)) { //加上范围后,不同点2
money = getRangeNumber(maxRange - minRange - 1, maxRange - minRange);
} else {
money = random.nextDouble() * max;
}
if (money < min) {
money = min;
}
money = (double) Math.floor(money * 100) / 100;
redPackage.remainSize--;
redPackage.remainMoney = redPackage.remainMoney - money;
return money;
}
public static double getRangeNumber(double min, double max) {
SecureRandom random = new SecureRandom();
return random.nextDouble() * (max - min) + min;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
RedPackage redPackage = new RedPackage(10, 100);
while (redPackage.remainSize != 0) {
RangeRedEnvelope.getWrappedRandomMoney(redPackage);
}
//System.out.println("-------");
}
}
}
运行10次后的结果
11.47 11.35 11.59 11.39 11.7 9.41 9.65 7.13 6.95 9.35 |totalCount=99.99
11.1 11.66 11.86 11.9 6.01 11.05 11.8 6.69 7.94 9.98 |totalCount=99.99
11.08 11.55 11.82 11.79 7.96 11.32 8.0 9.37 6.47 10.62 |totalCount=99.98
11.97 11.58 11.55 11.13 9.31 9.85 6.26 11.33 8.83 8.19 |totalCount=100.00
11.38 11.84 11.35 11.6 6.67 11.82 10.4 9.19 7.73 8.0 |totalCount=99.98
11.01 11.05 11.26 11.78 11.27 8.62 8.19 11.3 7.07 8.44 |totalCount=99.99
11.9 11.61 11.45 11.1 7.21 11.53 8.18 11.15 8.17 7.7 |totalCount=100.00
11.47 11.8 11.03 11.57 11.1 6.67 11.05 10.55 6.47 8.28 |totalCount=99.99
11.57 11.09 11.95 11.52 10.1 6.96 11.5 9.91 8.14 7.26 |totalCount=100.00
11.27 11.26 11.16 11.88 11.79 6.61 11.62 9.92 6.99 7.5 |totalCount=100.00
11.89 11.52 11.1 11.99 8.53 8.24 11.0 6.06 11.83 7.83 |totalCount=99.99
11.65 11.73 11.89 11.46 9.86 7.92 9.64 9.94 9.09 6.8 |totalCount=99.98
11.44 11.46 11.78 11.09 11.97 10.62 7.73 9.39 7.23 7.29 |totalCount=100.00
11.84 11.8 11.11 11.23 11.76 6.5 11.34 8.51 9.78 6.11 |totalCount=99.98
11.64 11.12 11.96 11.38 10.76 10.91 8.76 8.87 7.89 6.7 |totalCount=99.99
11.43 11.79 11.13 11.08 11.18 7.6 11.53 9.69 8.02 6.54 |totalCount=99.99
11.91 11.57 11.2 11.7 6.79 11.11 8.27 11.14 6.28 10.03 |totalCount=100.00
11.35 11.51 11.86 11.73 8.37 11.63 9.14 9.87 8.07 6.45 |totalCount=99.98
11.42 11.87 11.24 11.87 6.06 11.34 11.8 9.06 8.56 6.76 |totalCount=99.98
11.3 11.74 11.41 11.95 7.09 11.33 6.71 11.09 10.89 6.47 |totalCount=99.98
就范围红包的算法来说,刚开始需要取值比较大,会造成相对的不公平。
参考:
微信红包的随机算法是怎样实现的?