生成紅包算法

import java.util.Random;

public class HongBaoAlgorithm {
    static Random random = new Random();
    static {
        random.setSeed(System.currentTimeMillis());
    }

    public static void main(String[] args) {
        long max = 10;
        long min = 1;

        long[] result = HongBaoAlgorithm.generate(111, 22, max, min);
        long total = 0;
        for (int i = 0; i < result.length; i++) {
            // System.out.println("result[" + i + "]:" + result[i]);
            // System.out.println(result[i]);
            total += result[i];
        }
        //檢查生成的紅包的總額是否正確
        System.out.println("total:" + total);

        //統計每個錢數的紅包數量,檢查是否接近正態分佈
        int count[] = new int[(int) max + 1];
        for (int i = 0; i < result.length; i++) {
            count[(int) result[i]] += 1;
        }

        for (int i = 0; i < count.length; i++) {
            System.out.println("" + i + "  " + count[i]);
        }
    }

    /**
     * 生產min和max之間的隨機數,但是概率不是平均的,從min到max方向概率逐漸加大。
     * 先平方,然後產生一個平方值範圍內的隨機數,再開方,這樣就產生了一種“膨脹”再“收縮”的效果。
     *
     * @param min
     * @param max
     * @return
     */
    static long xRandom(long min, long max) {
        return sqrt(nextLong(sqr(max - min)));
    }

    /**
     *
     * @param total
     *            紅包總額
     * @param count
     *            紅包個數
     * @param max
     *            每個小紅包的最大額
     * @param min
     *            每個小紅包的最小額
     * @return 存放生成的每個小紅包的值的數組
     */
    public static long[] generate(long total, int count, long max, long min) {
        long[] result = new long[count];

        long average = total / count;

        long a = average - min;
        long b = max - min;

        //
        //這樣的隨機數的概率實際改變了,產生大數的可能性要比產生小數的概率要小。
        //這樣就實現了大部分紅包的值在平均數附近。大紅包和小紅包比較少。
        long range1 = sqr(average - min);
        long range2 = sqr(max - average);

        for (int i = 0; i < result.length; i++) {
            //因爲小紅包的數量通常是要比大紅包的數量要多的,因爲這裏的概率要調換過來。
            //當隨機數>平均值,則產生小紅包
            //當隨機數<平均值,則產生大紅包
            if (nextLong(min, max) > average) {
                // 在平均線上減錢
//              long temp = min + sqrt(nextLong(range1));
                long temp = min + xRandom(min, average);
                result[i] = temp;
                total -= temp;
            } else {
                // 在平均線上加錢
//              long temp = max - sqrt(nextLong(range2));
                long temp = max - xRandom(average, max);
                result[i] = temp;
                total -= temp;
            }
        }
        // 如果還有餘錢,則嘗試加到小紅包裏,如果加不進去,則嘗試下一個。
        while (total > 0) {
            for (int i = 0; i < result.length; i++) {
                if (total > 0 && result[i] < max) {
                    result[i]++;
                    total--;
                }
            }
        }
        // 如果錢是負數了,還得從已生成的小紅包中抽取回來
        while (total < 0) {
            for (int i = 0; i < result.length; i++) {
                if (total < 0 && result[i] > min) {
                    result[i]--;
                    total++;
                }
            }
        }
        return result;
    }

    static long sqrt(long n) {
        // 改進爲查表?
        return (long) Math.sqrt(n);
    }

    static long sqr(long n) {
        // 查錶快,還是直接算快?
        return n * n;
    }

    static long nextLong(long n) {
        return random.nextInt((int) n);
    }

    static long nextLong(long min, long max) {
        return random.nextInt((int) (max - min + 1)) + min;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章