红包——二倍均值和线段切割

本文深入解析两种红包分配算法:二倍均值法与线段切割法。前者通过随机生成不超过人均两倍金额的方式确保公平,后者则通过随机切割线段来分配金额,最终保证总和一致。文章附带Java实现代码,探讨了算法的公平性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每个人至少抢到一分钱。
所有人抢到金额之和等于红包金额,不能超过,也不能少于。
要保证所有人抢到金额的几率相等。
1.二倍均值

// 发红包算法,金额参数以分为单位
	public static List<Integer> divideRedPackage(Integer totalAmount, Integer totalPeopleNum) {

		List<Integer> amountList = new ArrayList<Integer>();

		Integer restAmount = totalAmount;

		Integer restPeopleNum = totalPeopleNum;

		Random random = new Random();

		for (int i = 0; i < totalPeopleNum - 1; i++) {

			// 随机范围:[1,剩余人均金额的两倍),左闭右开

			int amount = random.nextInt(restAmount / restPeopleNum * 2 - 1) + 1;
			restAmount -= amount;
			restPeopleNum--;
			amountList.add(amount);
		}
		amountList.add(restAmount);

		return amountList;
	}
public static void main(String[] args) {

List<Integer> amountList = divideRedPackage(10, 10);

		for (Integer amount : amountList) {

			System.out.println("抢到金额:" + new BigDecimal(amount).divide(new BigDecimal(100)));
		}
}

代码引用漫画讲解
2.线段切割

public static void main(String[] args) {
Integer totalAmount=1000;//红包金额  分
		Integer totalPeopleNum=10;//红包个数
		List<Integer> list=new ArrayList<Integer>();
		Random random = new Random();
		
		while (list.size()<=totalPeopleNum) {
			int i=random.nextInt(totalAmount)+1;//最低1分钱
			if(list.indexOf(i)<0){//非重复切割添加到集合
				list.add(i);
			}
		}
		Collections.sort(list);
		int  flag=0;
		int fl=0;
		for (int i=0;i<list.size();i++) {
			int temp=list.get(i)-flag;
			flag=list.get(i);
			fl+=temp;
			System.out.println("红包切割金额"+AmountUtil.div(temp,100));
		}
		//最后一个红包
		list.add(totalAmount-fl);
		System.out.println("红包切割金额"+AmountUtil.div(AmountUtil.sub(totalAmount, fl),100));
		
		System.out.println(fl+AmountUtil.sub(totalAmount, fl));
}

这里没有考虑集合排序的 时间复杂度和空间复杂度 。
AmountUtil 为工具类 代码:

package com.qingyu.pay.common.utils;

import java.math.BigDecimal;

/**
 * 金额计算工具
 * 
 * 
 * @author poll
 * @version $Id: AmountUtil.java, v 0.1 2016年11月9日 上午10:58:19 poll Exp $
 */
public class AmountUtil {

    /** 
     * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精  
     * 确的浮点数运算,包括加减乘除和四舍五入。  
     * 
     * 一:在 Java 中写入 new BigDecimal(0.1)
     * 但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。 
     * 这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。 
     * 这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。  
     * 二:另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal, 
     * 它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。  
     * 三:使用public static BigDecimal valueOf(double val) 
     * 使用 Double.toString(double) 方法提供的 double 规范的字符串表示形式将 double 转换为 BigDecimal。  
     * 这通常是将 double(或 float)转化为 BigDecimal 的首选方法, 
     * 因为返回的值等于从构造 BigDecimal(使用 Double.toString(double) 得到的结果)得到的值。  
     */

    //默认除法运算精度   
    private static final int DEF_DIV_SCALE = 2;

    //这个类不能实例化   

    private AmountUtil() {

    }

    public static BigDecimal toBigDecimal(double v) {
        return new BigDecimal(Double.valueOf(v));
    }

    public static double toDoubleValue(BigDecimal b) {
        if (b == null) {
            return 0.0;
        } else {
            return b.doubleValue();
        }
    }

    /**
     * 金额单位转为分
     * 
     * @param amt
     * @return
     */
    public static long unitToCent(double v) {

        BigDecimal a = new BigDecimal(Double.toString(v));
        BigDecimal b = BigDecimal.valueOf(100);

        return a.multiply(b).longValue();
    }

    /**
     * 金额单位转为元
     * 
     * @param amt
     * @return
     */
    public static double unitToYuan(long v) {

        BigDecimal b1 = new BigDecimal(Long.toString(v));

        BigDecimal b2 = new BigDecimal(Long.toString(100));

        return b1.divide(b2).doubleValue();
    }

    /** */
    /**  
    	 
     * 提供精确的加法运算。  
    	 
     * @param v1 被加数  
    	 
     * @param v2 加数  
    	 
     * @return 两个参数的和  
    	 
     */
    public static double add(double v1, double v2) {

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.add(b2).doubleValue();

    }

    /**  
    	 
     * 提供精确的减法运算。  
    	 
     * @param v1 被减数  
    	 
     * @param v2 减数  
    	 
     * @return 两个参数的差  
    	 
     */
    public static double sub(double v1, double v2) {

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.subtract(b2).doubleValue();

    }

    /**  
    	 
     * 提供精确的乘法运算。  
    	 
     * @param v1 被乘数  
    	 
     * @param v2 乘数  
    	 
     * @return 两个参数的积  
    	 
     */
    public static double mul(double v1, double v2) {

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.multiply(b2).doubleValue();

    }

    /** */
    /**  
    	 
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到  
    	 
     * 小数点以后2位,以后的数字四舍五入。  
    	 
     * @param v1 被除数  
    	 
     * @param v2 除数  
    	 
     * @return 两个参数的商  
    	 
     */
    public static double div(double v1, double v2) {

        return div(v1, v2, DEF_DIV_SCALE);

    }

    /**  
    	 
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指  
    	 
     * 定精度,以后的数字四舍五入。  
    	 
     * @param v1 被除数  
    	 
     * @param v2 除数  
    	 
     * @param scale 表示表示需要精确到小数点以后几位。  
    	 
     * @return 两个参数的商  
    	 
     */
    public static double div(double v1, double v2, int scale) {

        if (scale < 0) {

            throw new IllegalArgumentException("The scale must be a positive integer or zero");

        }

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

    /** */
    /**  
    	 
     * 提供精确的小数位四舍五入处理。  
    	 
     * @param v 需要四舍五入的数字  
    	 
     * @param scale 小数点后保留几位  
    	 
     * @return 四舍五入后的结果  
    	 
     */
    public static double round(double v, int scale) {

        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");

        }

        BigDecimal b = new BigDecimal(Double.toString(v));

        BigDecimal one = new BigDecimal("1");

        return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();

    }

    /**
     * if a==b return true, else return false;
     * @param a
     * @param b
     * @return
     */
    public static boolean equal(double a, double b) {
        if ((a - b > -0.001) && (a - b) < 0.001) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * if a>=b return true, else return false;
     * @param a
     * @param b
     * @return
     */
    public static boolean compare(double a, double b) {
        if (a - b > -0.001)
            return true;
        else
            return false;
    }

    /**
     * if a>b return true, else return false;
     * @param a
     * @param b
     * @return
     */
    public static boolean bigger(double a, double b) {
        if (a - b > 0.001)
            return true;
        else
            return false;
    }

    /**
     * 
     * 返回不小于 value(保留scale位小数)的下一个数。
     * <br>12.1310->12.14 
     * <br>12.13009->12.14
     * @param int scale 保留的小数位
     * @return double
     */
    public static double ceiling(double v, int scale) {

        if (scale < 0) {

            throw new IllegalArgumentException("The scale must be a positive integer or zero");

        }

        BigDecimal b = new BigDecimal(Double.toString(v));

        BigDecimal one = new BigDecimal("1");

        return b.divide(one, scale, BigDecimal.ROUND_UP).doubleValue();

    }

    /**
     * 
     * 返回不大于 value(保留scale位小数)的下一个数。
     * @param v
     * @param scale
     * @return
     */
    public static double tailing(double v, int scale) {

        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }

        BigDecimal b = new BigDecimal(Double.toString(v));

        BigDecimal one = new BigDecimal("1");

        return b.divide(one, scale, BigDecimal.ROUND_DOWN).doubleValue();

    }

    /**
     * 
     * 对精度后一位,进一取值
     * <br>12.1310->12.14
     * <br>12.1309->12.13
     * @param v 
     * @param scale 
     * @return
     */
    public static double increaseOne(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }

        BigDecimal b = new BigDecimal(Double.toString(v));

        return b.setScale(scale + 1, BigDecimal.ROUND_DOWN).setScale(scale, BigDecimal.ROUND_UP).doubleValue();
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值