紅包算法探討

最近在做一些微信紅包發放的功能,從而瞭解了一系列的紅包算法。這裏探討一下。給你一定的金額,給發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)));
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章