参考发红包的数值均匀分配算法+可设置最小值

最近有个需求是需要发红包的,正常如果是正常是最小值0.01,那么直接参考网上贴出来的微信红包算法即可。但是这个需求需要设置一个最小值。


    private BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100);

    private BigDecimal DEF_MIN = BigDecimal.valueOf(0.01);
    /**
     * 微信-均匀分配,最小值为0.01
     *@Author dong
     *@Date 2022/5/11 17:10
     *@param bean
     *@return
     */
    private BigDecimal doDivision(Bean bean){
        if (bean.remainSize == 1) {
            bean.remainSize--;
            return bean.remainMoney;
        }
        BigDecimal max = bean.remainMoney.divide(BigDecimal.valueOf(bean.remainSize),4,BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(2));
        BigDecimal money = RandomUtil.randomBigDecimal(max);
        money = money.compareTo(DEF_MIN)<0? DEF_MIN: money;
        money = money.multiply(ONE_HUNDRED).setScale(0,BigDecimal.ROUND_DOWN).divide(ONE_HUNDRED);
        bean.remainMoney = bean.remainMoney.subtract(money);
        bean.remainSize--;
        return money;
    }



    @AllArgsConstructor
    private class Bean{
        BigDecimal remainMoney;
        int remainSize;
    }

最初想法感觉很简单,这吧最小值改一下不就行了?但运行时发现这只能保证前面的几个数据正常。最后的红包可能会小于最小值甚至被扣成负数!折腾俩小时,终于是保证最小值的同时,又能实现均匀分配。完整代码:


import cn.hutool.core.util.RandomUtil;
import lombok.AllArgsConstructor;
import lombok.experimental.UtilityClass;

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

/**
 * 分割数字工具
 *
 * @Author: dong
 * @Date: 2022/5/11 11:34
 */
@UtilityClass
public class DivisionNumUtil {


    private BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100);

    private BigDecimal DEF_MIN = BigDecimal.valueOf(0.01);


    public static void main(String[] args) {
        System.out.println(division(BigDecimal.valueOf(100), 10));
        System.out.println(division(BigDecimal.valueOf(100), 10,BigDecimal.valueOf(9)));
    }


    /**
     * 分割,保留两位小数
     *@Author dong
     *@Date 2022/5/11 13:42
     *@param sum
     *@param count
     *@return
     */
    public List<BigDecimal> division(BigDecimal sum,int count) {
        return division(sum,count,null);
    }

    /**
     * 分割,保留两位小数
     * 如果sum-min*count<0.01*count,先去出count个min,剩余的随机下标分配
     * 否则 先分配出count个min-0.01,然后把剩余的值给再均匀分配
     *@Author dong
     *@Date 2022/5/11 13:42
     *@param sum
     *@param count
     *@param min 最小值
     *@return
     */
    public List<BigDecimal> division(BigDecimal sum,int count,BigDecimal min) {
        if(sum==null){
            throw new IllegalArgumentException("总数不能为空");
        }
        if(count<=0){
            throw new IllegalArgumentException("分割次数不能小于1");
        }
        if(min==null){
            min = DEF_MIN;
        }else if(min.compareTo(DEF_MIN)<0){
            throw new IllegalArgumentException("最小值不能小于0.01");
        }
        // 每个元素的基础数值
        BigDecimal base;
        // 待分配总数值
        BigDecimal divisionNum;
        // 计算出sum-min*count
        divisionNum = sum.subtract(min.multiply(BigDecimal.valueOf(count)));
        if(sum.compareTo(min.multiply(BigDecimal.valueOf(count)))<0){
            throw new IllegalArgumentException("总数不够分配");
        }
        List<BigDecimal> result = new ArrayList<>(count);
        // 这里算一下,sum-min*count之后,是否还能走微信的均匀分配
        if(divisionNum.compareTo(DEF_MIN.multiply(BigDecimal.valueOf(count)))<0){
            // 不能微信均匀分配,按最小单位分配到随机下标。基础数值=min
            base = min;
            for (int i=0;i<count;i++){
                result.add(base);
            }
            // 待分配数
            while (divisionNum.compareTo(DEF_MIN)>=0){
                divisionNum = divisionNum.subtract(DEF_MIN);
                int i = RandomUtil.randomInt(count);
                BigDecimal decimal = result.get(i);
                result.set(i,decimal.add(DEF_MIN));
            }
        }else{
            // 微信-红包均匀分配,基础数值=min-0.01。
            base = min.subtract(DEF_MIN);
            // 待分配=sum-基础数值*count
            divisionNum = sum.subtract(base.multiply(BigDecimal.valueOf(count)));
            Bean bean = new Bean(divisionNum,count);
            for (int i=0;i<count;i++){
                result.add(doDivision(bean).add(base));
            }
        }
        return result;
    }


    /**
     * 微信-均匀分配,最小值为0.01
     *@Author dong
     *@Date 2022/5/11 17:10
     *@param bean
     *@return
     */
    private BigDecimal doDivision(Bean bean){
        if (bean.remainSize == 1) {
            bean.remainSize--;
            return bean.remainMoney;
        }
        BigDecimal max = bean.remainMoney.divide(BigDecimal.valueOf(bean.remainSize),4,BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(2));
        BigDecimal money = RandomUtil.randomBigDecimal(max);
        money = money.compareTo(DEF_MIN)<0? DEF_MIN: money;
        money = money.multiply(ONE_HUNDRED).setScale(0,BigDecimal.ROUND_DOWN).divide(ONE_HUNDRED);
        bean.remainMoney = bean.remainMoney.subtract(money);
        bean.remainSize--;
        return money;
    }



    @AllArgsConstructor
    private class Bean{
        BigDecimal remainMoney;
        int remainSize;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值