1. 程式人生 > >關於微信手氣紅包算法的探討

關於微信手氣紅包算法的探討

繼續 ret 數據 max oat 般的 配方法 前言 閑來無事

關於微信手氣紅包算法的探討

前言

這大過年的,長輩家人朋友發的紅包搶到手軟,嘿嘿嘿。昨晚,和一個朋友出去浪,剛好兩人現在就讀專業相同,不知不覺間就談到了微信紅包的算法。今天閑來無事,就簡單的做了一下記錄。

第一種

這個是最簡單的實現,就是先給每個紅包分配0.01(確保大家都有),然後各個紅包輪流產生一個隨機數(不得大於當前總金額)並從總金額中取出相應的數,最後一個紅包不管如何,全盤接收總金額。
貼 java 代碼:

```java
    /**
     * 最簡單的分配方法,單位全部統一為 分
     * @param number 紅包總數
     * @param money 金額總數
     */
    public void redPacket1 (int number, int money){
        int minCount = 1;   //最低金額
        int moneyCount = money*100 - minCount*number;   //分配給各個紅包 0.01後可分配金額
        Random random = new Random();
        float[] array = new float[number];  //儲存紅包金額的數組
        StringBuffer arrayStr = new StringBuffer("");   //為了方便顯示
        //為前 number-1 個紅包分配金額
        for (int i=0; i<number-1; i++){
            try {
                array[i] = (minCount+random.nextInt(moneyCount)) ;
            } catch (IllegalArgumentException e){   //防止 moneyCount 為0
                array[i] = minCount ;
            }
            if (moneyCount!=0)  //可分配金額可以繼續分配
                moneyCount -= array[i];
            arrayStr.append(String.valueOf((double) array[i]/100) + " ");
        }
        array[number-1] = moneyCount+minCount;
        arrayStr.append(String.valueOf((double) array[number-1]/100) + " ");
        System.out.println(arrayStr);   //將各個紅包金額打印出來
    }

測試貼圖:
假設有個20塊的手氣紅包,分配為10個,(不考慮邊界數據的情況)
技術分享圖片

從結果中可以很清楚看出它的缺陷:
* 波動太大;
* 先搶的人往往會占據大部分紅包。

不過,我覺得這種算法還是存在的,一般適應於大紅包少人的情況:


技術分享圖片
我懷疑我搶了個假紅包!!!

第二種

第二種主要就是給隨機數增加一個上限,我朋友說可以使用總金額平均數來控制波動,我覺得可以改為使用平均數的某個倍數(我限制為1.0-2.9之間波動)。畢竟,搶紅包體現出歐皇氣質也不失為一種極大的樂趣!
同時,因為 java 中的 Random() 是一個偽隨機數,為了防止倍數與金額隨機數的影響(程序猿癖好),我重新 new 了個對象和封裝為方法使用。
貼 java 代碼:

 /**
     * 分配方法二,單位全部統一為 分
     * @param number 紅包總數
     * @param money 金額總數
     */
    public void redPacket2(int number, int money){
        Random random = new Random();
        float times = (float)random.nextInt(20)/10 + 1; //獲取一個倍數
        StringBuffer buffer = new StringBuffer();
        int minCount = 1;
        int moneyCount = money * 100;   //這次是金額總數
        int max = (int)(moneyCount*times/number);   //可分配最大值
        for(int i=0; i<number; i++){
            int packet = randomPacket(minCount, max, number-i, moneyCount);
            moneyCount -= packet;
            buffer.append(String.valueOf((double) packet/100) + " ");
        }
        buffer.append(" 倍數為:"+times);
        System.out.println(buffer);
    }

封裝的方法:

  /**
     * 分配一個紅包
     * @param minCount
     * @param maxCount
     * @param number
     * @param money
     * @return
     */
    private int randomPacket(int minCount, int maxCount, int number, int money){
        Random random = new Random();
        if (number == 1) return money;  //最後一個紅包
        if (minCount == maxCount) return minCount;  //最大隨機數與最小隨機數相同
        int max = maxCount>money ? money : maxCount;    //若倍數最大值大於現在的金額時,動態改變波動上限
        int packet = minCount;
        try {
            packet += random.nextInt(max-minCount);
        } catch (IllegalArgumentException e){   //還是可能出現 max-minCount 為 0 的情況
            packet += 0;
        }
        return packet;
    }

測試貼圖:
假設有個20塊的手氣紅包,分配為10個,(不考慮邊界數據的情況)
技術分享圖片
這次許多數值就比較正常,但還是有缺陷的,我覺得微信紅包出現 0.01 的概率比較大,但這種算法得出 0.01 的概率不是一般的小。Ps:100次測試中出現只出現了一次0.01。

最後

我自己覺得微信搶紅包的算法肯定不止一種,應該是在某個金額和紅包數時選擇某種更具趣味性的算法。知乎上也有一個關於紅包算法的討論,也有許多可取之處。

關於微信手氣紅包算法的探討