Java概率抽獎算法實現,歡迎拍磚

背景

最近看項目中的概率抽獎算法實現,總感覺有問題,驗證了多遍,發現沒問題。但是代碼不夠精簡,尤其使用了多次Random,這個還是可以優化的~

那麼,如何實現高效的概率算法?如何衡量算法的準確性呢?

算法實現

特點:只隨機一次,找出命中概率的 index。

// 入參示例:int[] array = {30, 40, 20, 10};
private static int getIndex(int[] array) {
    int num = new Random().nextInt(100);
    for (int i = 0; i < array.length; i++) {
        if (num < array[i]) {
            return i;
        }
        num -= array[i];
    }
    return -1;
}

如何衡量

// 計算概率,測試10000次
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < 10000; i++) {
    // 單次獲取概率結果
    int index = getIndex(array);
    // System.out.printf("中獎啦! 概率: %d, Index: %d%n", array[index], index);

    Integer value = map.getOrDefault(array[index], 0);
    map.put(array[index], value + 1);
}

System.out.println("----------------------------------------------");
System.out.printf("看看結果吧! 概率: %s%n", map);
System.out.println("----------------------------------------------");

觀察如下結果,符合按概率隨機的要求。

----------------------------------------------
看看結果吧! 概率: {20=2004, 40=3985, 10=1036, 30=2975}
----------------------------------------------
看看結果吧! 概率: {20=2053, 40=3932, 10=999, 30=3016}
----------------------------------------------
看看結果吧! 概率: {20=2044, 40=4015, 10=962, 30=2979}
----------------------------------------------
看看結果吧! 概率: {20=2040, 40=3955, 10=982, 30=3023}
----------------------------------------------
看看結果吧! 概率: {20=1968, 40=3976, 10=1009, 30=3047}
----------------------------------------------
看看結果吧! 概率: {20=1984, 40=4029, 10=989, 30=2998}
----------------------------------------------

健壯性

如果入參的總概率不滿100%,那剩餘的比例不能中獎,怎麼實現呢?

  1. 總概率不滿100%時,補全數組;
  2. 計算中獎的時候,再捨去補全的最後一組(當然,如果沒補就不用捨去)。

完整代碼如下:

package cn.eyeo.mall.start.lottery;

import org.junit.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 抽獎測試類
 *
 * @author <a href="mailto:[email protected]">amos.wang</a>
 * @date 2023/3/27
 */
public class LotteryDrawTests {

    private static final int TOTAL_PROBABILITY = 100;

    @Test
    public void lottery() {
        int[] array = {30, 40, 20};

        probability(array);
    }

    private static void probability(int[] source) {
        int sum = Arrays.stream(source).sum();
        if (sum > TOTAL_PROBABILITY) {
            throw new IllegalArgumentException("總概率不能>100");
        }

        System.out.println("----------------------------------------------");
        System.out.println("原始數據: " + Arrays.toString(source));

        // 不滿100%則補全
        int[] array = source;
        if (sum < TOTAL_PROBABILITY) {
            array = new int[source.length + 1];
            System.arraycopy(source, 0, array, 0, source.length);
            array[source.length] = TOTAL_PROBABILITY - sum;
        }
        System.out.println("補全後的數據: " + Arrays.toString(array));

        // 計算概率,測試10000次
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < 10000; i++) {
            // 單次獲取概率結果
            int index = getIndex(array);
            if (index < source.length) {
                // System.out.printf("中獎啦! 概率: %d, Index: %d%n", array[index], index);

                Integer value = map.getOrDefault(array[index], 0);
                map.put(array[index], value + 1);
            }
        }

        System.out.println("----------------------------------------------");
        System.out.printf("看看結果吧! 概率: %s%n", map);
    }

    private static int getIndex(int[] array) {
        int num = new Random().nextInt(TOTAL_PROBABILITY);
        for (int i = 0; i < array.length; i++) {
            if (num < array[i]) {
                return i;
            }
            num -= array[i];
        }
        return -1;
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章