1. 程式人生 > >模擬搶紅包中的發紅包

模擬搶紅包中的發紅包

本例子來源於程式設計師小灰微信公眾號上面的文章中講到的二倍均值法。
原文連結:https://mp.weixin.qq.com/s/AIE33sdT2QI6UL8cs1kJCQ

剩餘紅包金額為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元。
以此類推,每一次隨機範圍的均值是相等的。

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 模擬搶紅包
 * @author pilaf
 * @create: 2018-10-31 08:40
 */
public class Demo {

    /**
     * 分配紅包
     *
     * @param totalPeopleNum 參與搶紅包的總人數
     * @param totalAmount    紅包總金額(以分為單位)
     * @return
     */
public static List<Integer> divideRedPackage(int totalPeopleNum, int totalAmount) { List<Integer> amountList = new ArrayList<>(); //剩餘的紅包總金額 int restTotalAmount = totalAmount; //剩餘的人數 int restPeopleNum = totalPeopleNum; Random random =
new Random(); for (int i = 0; i < totalPeopleNum - 1; i++) { //隨機範圍:[1,剩餘人均金額的兩倍),左閉右開 int amount = random.nextInt(restTotalAmount * 2 / restPeopleNum - 1) + 1; restPeopleNum--; restTotalAmount -= amount; amountList.add(amount); } amountList.add(restTotalAmount); return amountList; } public static void main(String[] args) { //10個人分10塊錢紅包 List<Integer> amountList = divideRedPackage(10, 10 * 100); BigDecimal sum = new BigDecimal("0"); for (Integer amount : amountList) { BigDecimal money = new BigDecimal(amount).divide(new BigDecimal(100)); //注意這兒,一定要把sum.add(money)再賦值給sum,否則迴圈外面的sum會是0。因為BigDecimal是Immutable類。 sum = sum.add(money); System.out.println("紅包金額:" + money + "元"); } //看看發出去的紅包總額是否真的等於收到的紅包總額 System.out.println("收到的紅包總金額:" + sum+"元"); } }

執行一次的輸出為:

紅包金額:1.38元
紅包金額:0.05元
紅包金額:1.42元
紅包金額:1.61元
紅包金額:0.27元
紅包金額:0.75元
紅包金額:1.17元
紅包金額:1.94元
紅包金額:0.92元
紅包金額:0.49元
收到的紅包總金額:10.00

以前一直以為BigDecimal是可變的,寫demo的時候執行時,一開始並沒有把for迴圈中的sum.add(money)結果重新賦值給sum

sum = sum.add(money);

而是

sum.add(money);

導致最後在for迴圈外輸出收到的紅包總金額一直是零,查看了BigDecimal原始碼中的註釋:

Immutable, arbitrary-precision signed decimal numbers. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. The value of the number represented by the BigDecimal is therefore (unscaledValue × 10-scale).

才想起來BigDecimal是不可變類,就像String類一樣。