生成随机红包的一个工具

560 阅读2分钟

一个支持实时计算随机红包的工具,支持最大值Integer.MAX

参考链接:www.cnblogs.com/hanganglin/…

package com.example.demo;


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



/**
 * @author zhanglu
 * @date Created in 2020/8/18 14:45
 * @description
 * @modifiedBy
 * @version:
 */


public class hongbao4 {

    /**
     * 返回一次抽奖在指定中奖概率下是否中奖
     *
     * @param rate 中奖概率
     * @return
     */
    public static boolean canReward(double rate) {
        return Math.random() <= rate;
    }

    /**
     * 返回min~max区间内随机数,含min和max
     *
     * @param min
     * @param max
     * @return
     */
    private static int getRandomVal(int min, int max) {
        Random random = new Random();
        return random.nextInt(max - min + 1) + min;
    }

    /**
     * 红包 合法性校验
     *
     * @param money
     * @param count
     * @return
     */
    private static boolean isRight(int money, int count, int min, int max) {
        double avg = money / count;
        //小于最小金额
        if (avg < min) {
            return false;
            //大于最大金额
        } else if (avg > max) {
            return false;
        }
        return true;
    }

    /**
     * 带概率偏向的随机算法,概率偏向subMin~subMax区间
     * 返回boundMin~boundMax区间内随机数(含boundMin和boundMax),同时可以指定子区间subMin~subMax的优先概率
     * 例:传入参数(10, 50, 20, 30, 0.8),则随机结果有80%概率从20~30中随机返回,有20%概率从10~50中随机返回
     *
     * @param boundMin 边界
     * @param boundMax
     * @param subMin
     * @param subMax
     * @param subRate
     * @return
     */
    public static int getRandomValWithSpecifySubRate(int boundMin, int boundMax, int subMin, int subMax, double subRate) {
        if (canReward(subRate)) {
            return getRandomVal(subMin, subMax);
        }
        return getRandomVal(boundMin, boundMax);
    }

    /**
     * 随机分配第n个红包
     *
     * @param totalBonus  总红包量
     * @param totalNum    总份数
     * @param sendedBonus 已发送红包量
     * @param sendedNum   已发送份数
     * @param rdMin       随机下限
     * @param rdMax       随机上限
     * @return
     */
    private static Integer randomBonusWithSpecifyBound(Integer totalBonus, Integer totalNum, Integer sendedBonus,
                                                       Integer sendedNum, Integer rdMin, Integer rdMax, double bigRate) {
        if (!isRight(totalBonus, totalNum, rdMin, rdMax)) {
            return null;
        }

        Integer avg = totalBonus / totalNum;  // 平均值
        Integer leftLen = avg - rdMin;
        Integer rightLen = rdMax - avg;
        Integer boundMin = 0, boundMax = 0;

        // 大范围设置小概率
        if (leftLen.equals(rightLen)) {
            boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), rdMin);
            boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), rdMax);
        } else if (rightLen.compareTo(leftLen) > 0) {
            // 上限偏离
            Integer standardRdMax = avg + leftLen;  // 右侧对称上限点
            Integer _rdMax = canReward(bigRate) ? rdMax : standardRdMax;
            boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMax), rdMin);
            boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), _rdMax);
        } else {
            // 下限偏离
            Integer standardRdMin = avg - rightLen;  // 左侧对称下限点
            Integer _rdMin = canReward(bigRate) ? rdMin : standardRdMin;
            boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), _rdMin);
            boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMin), rdMax);
        }

        // 已发平均值偏移修正-动态比例
        if (boundMin.equals(boundMax)) {
            return getRandomVal(boundMin, boundMax);
        }
        double currAvg = sendedNum == 0 ? (double) avg : (sendedBonus / (double) sendedNum);  // 当前已发平均值
        double middle = (boundMin + boundMax) / 2.0;
        Integer subMin = boundMin, subMax = boundMax;
        // 期望值
        double exp = avg - (currAvg - avg) * sendedNum / (double) (totalNum - sendedNum);
        if (middle > exp) {
            subMax = (int) Math.round((boundMin + exp) / 2.0);
        } else {
            subMin = (int) Math.round((exp + boundMax) / 2.0);
        }
        Integer expBound = (boundMin + boundMax) / 2;
        Integer expSub = (subMin + subMax) / 2;
        double subRate = (exp - expBound) / (double) (expSub - expBound);
        return getRandomValWithSpecifySubRate(boundMin, boundMax, subMin, subMax, subRate);
    }


    /**
     * 生成红包一次分配结果
     *
     * @param totalBonus 红包总金额
     * @param totalNum   红包总数量
     * @param rdMin      红包随机最大值
     * @param rdMax      红包随机最小值
     * @param bigRate    指定大范围区间的概率
     * @return
     */
    public static List<Integer> createBonusList(Integer totalBonus, Integer totalNum, Integer rdMin, Integer rdMax, double bigRate) {
        Integer sendedBonus = 0;
        Integer sendedNum = 0;
        List<Integer> bonusList = new ArrayList<>();
        while (sendedNum < totalNum) {
            Integer bonus = randomBonusWithSpecifyBound(totalBonus, totalNum, sendedBonus, sendedNum, rdMin, rdMax, bigRate);
            if (null == bonus) {
                return bonusList;
            }
            bonusList.add(bonus);
            sendedNum++;
            sendedBonus += bonus;
        }
        return bonusList;
    }

    public static void main(String[] arg) {
    List<Integer> bonusList = createBonusList(20000, 200, 1, 500, 0.8);
    System.out.println(bonusList);
    Integer totle = 0;
    for (Integer i : bonusList) {
      totle = totle + i;
    }
    System.out.println("总计发出红包金额:" + totle);

    }

}