最近在做一些微信紅包發放的功能,從而瞭解了一系列的紅包算法。這裏探討一下。給你一定的金額,給發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)));
}
}
}