1. 程式人生 > >漫畫:如何實現搶紅包演算法?

漫畫:如何實現搶紅包演算法?

點選上方“程式設計師小灰”,選擇“置頂公眾號”

有趣有內涵的文章第一時間送達!

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&retryload=1

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&retryload=1

640?wx_fmt=png

640?wx_fmt=jpeg

640?wx_fmt=jpeg

發出一個固定金額的紅包,由若干個人來搶,需要滿足哪些規則?

1.所有人搶到金額之和等於紅包金額,不能超過,也不能少於。

2.每個人至少搶到一分錢。

3.要保證所有人搶到金額的機率相等。

640?wx_fmt=jpeg

小灰的思路是什麼樣呢?

每次搶到的金額 = 隨機區間 ( 0,  剩餘金額 )

640?wx_fmt=jpeg

為什麼這麼說呢?讓我們看一個栗子:

假設有10個人,紅包總額100元。

第一個人的隨機範圍是(0,100元),平均可以搶到50元

假設第一個人隨機到50元,那麼剩餘金額是100-50 = 50 元。

第二個人的隨機範圍是 (0, 50元),平均可以搶到25元

假設第二個人隨機到25元,那麼剩餘金額是50-25 = 25 元。

第三個人的隨機範圍是 (0, 25元),平均可以搶到12.5元

以此類推,每一次隨機範圍越來越小。

640?wx_fmt=jpeg

640?wx_fmt=jpeg

方法1:二倍均值法

剩餘紅包金額為M,剩餘人數為N,那麼有如下公式:

每次搶到的金額 = 隨機區間 (0, M / N X 2)

這個公式,保證了每次隨機金額的平均值是相等的,不會因為搶紅包的先後順序而造成不公平。

舉個栗子:

假設有10個人,紅包總額100元。

100/10X2 = 20, 所以第一個人的隨機範圍是(0,20 ),平均可以搶到10元

假設第一個人隨機到10元,那麼剩餘金額是100-10 = 90 元。

90/9X2 = 20, 所以第二個人的隨機範圍同樣是(0,20 ),平均可以搶到10元

假設第二個人隨機到10元,那麼剩餘金額是90-10 = 80 元。

80/8X2 = 20, 所以第三個人的隨機範圍同樣是(0,20 ),平均可以搶到10元

以此類推,每一次隨機範圍的均值是相等的。

640?wx_fmt=jpeg

  1. //發紅包演算法,金額引數以分為單位

  2. publicstaticList<Integer> divideRedPackage(Integer totalAmount, Integer totalPeopleNum){

  3. List<Integer> amountList = newArrayList<Integer>();

  4. Integer restAmount = totalAmount;

  5. Integer restPeopleNum = totalPeopleNum;

  6. Random random = newRandom();

  7. for(int i=0; i<totalPeopleNum-1; i++){

  8. //隨機範圍:[1,剩餘人均金額的兩倍),左閉右開

  9. int amount = random.nextInt(restAmount / restPeopleNum * 2 - 1) + 1;

  10.        restAmount -= amount;

  11.        restPeopleNum --;

  12.        amountList.add(amount);

  13.    }

  14.    amountList.add(restAmount);

  15. return amountList;

  16. }

  17. publicstaticvoid main(String[] args){

  18. List<Integer> amountList = divideRedPackage(5000, 30);

  19. for(Integer amount : amountList){

  20. System.out.println("搶到金額:" + newBigDecimal(amount).divide(newBigDecimal(100)));

  21.    }

  22. }

程式輸出結果如下:

搶到金額:2.92

搶到金額:1.48

搶到金額:3.05

搶到金額:0.53

搶到金額:0.45

搶到金額:1.36

搶到金額:1.02

搶到金額:1.99

搶到金額:1.3

搶到金額:0.48

搶到金額:0.83

搶到金額:2.89

搶到金額:0.94

搶到金額:2.11

搶到金額:3.13

搶到金額:0.91

搶到金額:2.64

搶到金額:2.02

搶到金額:2.88

搶到金額:1.13

搶到金額:2.09

搶到金額:1.37

搶到金額:2.41

搶到金額:2.13

搶到金額:1.32

搶到金額:0.44

搶到金額:1.62

搶到金額:1.89

搶到金額:2.23

搶到金額:0.44

640?wx_fmt=jpeg

640?wx_fmt=jpeg

640?wx_fmt=jpeg

640?wx_fmt=jpeg

方法2:線段切割法

何謂線段切割法?我們可以把紅包總金額想象成一條很長的線段,而每個人搶到的金額,則是這條主線段所拆分出的若干子線段。

640?wx_fmt=png

如何確定每一條子線段的長度呢?由“切割點”來決定。當N個人一起搶紅包的時候,就需要確定N-1個切割點。

因此,當N個人一起搶總金額為M的紅包時,我們需要做N-1次隨機運算,以此確定N-1個切割點。隨機的範圍區間是(1, M)。

當所有切割點確定以後,子線段的長度也隨之確定。這樣每個人來搶紅包的時候,只需要順次領取與子線段長度等價的紅包金額即可。

這就是線段切割法的思路。在這裡需要注意以下兩點:

1.當隨機切割點出現重複,如何處理。

2.如何儘可能降低時間複雜度和空間複雜度。

640?wx_fmt=jpeg

640?wx_fmt=jpeg

—————END—————

640?wx_fmt=png

喜歡本文的朋友們,歡迎長按下圖關注訂閱號程式設計師小灰,收看更多精彩內容

640?wx_fmt=jpeg