最近在做一些微信红包发放的功能,从而了解了一系列的红包算法。这里探讨一下。给你一定的金额,给发n个红包。
随机数发红包
使用随机数,每次先给红包塞入1分钱,然后在剩余的红包余额里面随机一个金额加入红包内。
缺点:不公平,越后面越亏,越后面的随机数越少,非常不平衡。抢红包抢到心态爆炸。
package 红包;
import java.util.Arrays;
import java.util.Random;
public class Test1 {
/**
* 越后面的随机数越少,非常不平衡。抢红包抢到心态爆炸。
* @param args
*/
public static void main(String[] args) {
int[] a = divide(100,20);
int max = 0;
int second = 0;
for (int item : a) {
if (item > max)
max = item;
if (item <max && item >second)
second = item;
System.out.println(item);
}
System.out.println(max);
System.out.println(second);
}
private static int[] divide(double money, int n) {
//验证参数合理校验
int fen = (int)(money*100);
if (fen < n || n < 1) {
System.out.println("红包个数必须大于0,并且最小红包不少于1分");
}
int[] a = new int[n];
//先给每个红包中塞入1分
Arrays.fill(a, 1);
fen -= n;
Random r = new Random();
while (fen>1) {
//最后一次剩余fen=1,fen===0
int f = r.nextInt(fen);
int index = r.nextInt(a.length);
a[index] += f;
fen -= f;
}
if (fen > 0) {
a[0] += fen;
}
return a;
}
}
线段切割法
相当于把总金额都变成1分分的钞票,随机使用木板插入里面,保证木板不重叠以及里面至少有1分钱即可。
优点:公平
缺点:发个数少的话,真.随机,差距大。
package 红包;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.border.Border;
public class Test2 {
/**
* 比较公平的方式,线段切割法。
* 当发的红包个数大的时候就感觉挺公平的。发的少浮动大。
* @param args
*/
public static void main(String[] args) {
List<Integer> a = divide(1000,5000);
int max = 0;
int second = 0;
int min = 0;
for (int item : a) {
if (item > max)
max = item;
if (item <max && item >second)
second = item;
if (item == 1)
min += 1;
// System.out.println(item);
}
System.out.println(max);
System.out.println(second);
System.out.println("min的个数为" + min);
}
private static List<Integer> divide(double money, int n) {
//验证参数合理校验
int fen = (int)(money*100);
if (fen < n || n < 1) {
System.out.println("红包个数必须大于0,并且最小红包不少于1分");
}
List<Integer> boards = new ArrayList<>();
boards.add(0);
boards.add(fen);
//红包个数和板砖个数的关系
while (boards.size() < n+1) {
int index = new Random().nextInt(fen-1)+1;
if (boards.contains(index)) {
//保证板子的位置不相同
continue;
}
boards.add(index);
}
//计算每个红包的金额,将两个板子之间的钱加起来
Collections.sort(boards);
List<Integer> list = new ArrayList<>();
for (int i = 0; i < boards.size()-1; i++) {
Integer e = boards.get(i+1) - boards.get(i);
list.add(e);
}
return list;
}
}
微信红包策略
符合正态分布,最少一分,最多2*平均值-1
,但是保证了落差不会太大,心态没那么容易爆炸。
package 红包;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Test3 {
// 发红包算法,金额参数以分为单位
/** https://mp.weixin.qq.com/s/7yDbdKHJ3OmNw_015Jc8Cg
* 除了最后一次,任何一次都少过人均两倍。但是保证了落差不会太大,心态没那么容易爆炸。
* @param totalAmount 分
* @param totalPeopleNum 发放个数
* @return
*/
public static List<Integer> divideRedPackage(Integer totalAmount,
Integer totalPeopleNum) {
List<Integer> amountList = new ArrayList<Integer>();
Integer restAmount = totalAmount;
Integer restPeopleNum = totalPeopleNum;
Random random = new Random();
for (int i = 0; i < totalPeopleNum - 1; i++) {
// 随机范围:[1,剩余人均金额的两倍),左闭右开
int amount = random.nextInt(restAmount / restPeopleNum * 2 - 1) + 1;
restAmount -= amount;
restPeopleNum--;
amountList.add(amount);
}
amountList.add(restAmount);
return amountList;
}
public static void main(String[] args) {
List<Integer> amountList = divideRedPackage(10000, 20);
for (Integer amount : amountList) {
System.out.println("抢到金额:"
+ new BigDecimal(amount).divide(new BigDecimal(100)));
}
}
}