|
@@ -0,0 +1,205 @@
|
|
|
|
|
+package com.template.common.utils;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.math.RoundingMode;
|
|
|
|
|
+import java.sql.SQLOutput;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Random;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * @Author: xwt
|
|
|
|
|
+ * @Date: 2025/12/30 星期二 9:28
|
|
|
|
|
+ * @Description: com.template.common.utils
|
|
|
|
|
+ * @Version: 1.0
|
|
|
|
|
+ */
|
|
|
|
|
+public class RedPacket {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成随机红包
|
|
|
|
|
+ * @param totalAmount 总金额(单位:元)
|
|
|
|
|
+ * @param peopleCounts 人数
|
|
|
|
|
+ * @return 每人分得的金额列表(单位:元)
|
|
|
|
|
+ */
|
|
|
|
|
+ public static List<Double> generateRedPacket(double totalAmount, List<String> peopleCounts) {
|
|
|
|
|
+ if (totalAmount <= 0 || peopleCounts.size() <= 0) {
|
|
|
|
|
+ throw new IllegalArgumentException("金额和人数必须大于0");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (totalAmount < peopleCounts.size() * 0.01) {
|
|
|
|
|
+ throw new IllegalArgumentException("金额不足以分配给所有人");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<Double> result = new ArrayList<>();
|
|
|
|
|
+ Random random = new Random();
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为分,避免浮点数精度问题
|
|
|
|
|
+ int remainingAmount = (int)(totalAmount * 100);
|
|
|
|
|
+ int remainingPeople = peopleCounts.size();
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < peopleCounts.size() - 1; i++) {
|
|
|
|
|
+ // 确保每个人至少能分到0.01元
|
|
|
|
|
+ // 随机范围:[1, 剩余金额/剩余人数 * 2]
|
|
|
|
|
+ int max = remainingAmount / remainingPeople * 2;
|
|
|
|
|
+ int amount = random.nextInt(max - 1) + 1;
|
|
|
|
|
+
|
|
|
|
|
+ // 确保不会因为随机导致后面的人无法分配
|
|
|
|
|
+ amount = Math.max(1, Math.min(amount, remainingAmount - remainingPeople + 1));
|
|
|
|
|
+
|
|
|
|
|
+ result.add(amount / 100.0);
|
|
|
|
|
+ remainingAmount -= amount;
|
|
|
|
|
+ remainingPeople--;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 最后一个人获得剩余所有金额
|
|
|
|
|
+ result.add(remainingAmount / 100.0);
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 使用BigDecimal精确计算的版本
|
|
|
|
|
+ */
|
|
|
|
|
+ public static List<BigDecimal> generateRedPacketPrecise(BigDecimal totalAmount, List<String> peopleCounts) {
|
|
|
|
|
+ if (totalAmount.compareTo(BigDecimal.ZERO) <= 0 || peopleCounts.size() <= 0) {
|
|
|
|
|
+ throw new IllegalArgumentException("金额和人数必须大于0");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ BigDecimal minAmount = new BigDecimal("0.01");
|
|
|
|
|
+ if (totalAmount.compareTo(minAmount.multiply(new BigDecimal(peopleCounts.size()))) < 0) {
|
|
|
|
|
+ throw new IllegalArgumentException("金额不足以分配给所有人");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<BigDecimal> result = new ArrayList<>();
|
|
|
|
|
+ Random random = new Random();
|
|
|
|
|
+
|
|
|
|
|
+ BigDecimal remainingAmount = totalAmount;
|
|
|
|
|
+ int remainingPeople = peopleCounts.size();
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < peopleCounts.size() - 1; i++) {
|
|
|
|
|
+ // 计算最大可分配金额:剩余金额除以剩余人数乘以2
|
|
|
|
|
+ BigDecimal avg = remainingAmount.divide(new BigDecimal(remainingPeople), 10, RoundingMode.HALF_UP);
|
|
|
|
|
+ BigDecimal max = avg.multiply(new BigDecimal(2));
|
|
|
|
|
+
|
|
|
|
|
+ // 生成随机金额(0.01 ~ max)
|
|
|
|
|
+ BigDecimal randomValue = new BigDecimal(random.nextDouble())
|
|
|
|
|
+ .multiply(max.subtract(minAmount))
|
|
|
|
|
+ .add(minAmount);
|
|
|
|
|
+
|
|
|
|
|
+ // 保留两位小数
|
|
|
|
|
+ randomValue = randomValue.setScale(2, RoundingMode.HALF_UP);
|
|
|
|
|
+
|
|
|
|
|
+ // 确保剩余金额足够分配
|
|
|
|
|
+ BigDecimal minRequired = minAmount.multiply(new BigDecimal(remainingPeople - 1));
|
|
|
|
|
+ if (remainingAmount.subtract(randomValue).compareTo(minRequired) < 0) {
|
|
|
|
|
+ randomValue = remainingAmount.subtract(minRequired);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ result.add(randomValue);
|
|
|
|
|
+ remainingAmount = remainingAmount.subtract(randomValue);
|
|
|
|
|
+ remainingPeople--;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 最后一个人获得剩余金额
|
|
|
|
|
+ result.add(remainingAmount.setScale(2, RoundingMode.HALF_UP));
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 简单的二倍均值法
|
|
|
|
|
+ */
|
|
|
|
|
+ public static List<Double> simpleRedPacket(double totalAmount, List<String> peopleCounts) {
|
|
|
|
|
+ List<Double> result = new ArrayList<>();
|
|
|
|
|
+ Random random = new Random();
|
|
|
|
|
+ double remaining = totalAmount;
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < peopleCounts.size() - 1; i++) {
|
|
|
|
|
+ // 二倍均值法:随机范围在 [0.01, 剩余金额/剩余人数 * 2 - 0.01]
|
|
|
|
|
+ double max = remaining / (peopleCounts.size() - i) * 2;
|
|
|
|
|
+ double money = random.nextDouble() * max;
|
|
|
|
|
+
|
|
|
|
|
+ // 确保不低于0.01元
|
|
|
|
|
+ money = Math.max(0.01, money);
|
|
|
|
|
+ money = Math.floor(money * 100) / 100.0;
|
|
|
|
|
+
|
|
|
|
|
+ result.add(money);
|
|
|
|
|
+ remaining -= money;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 最后一个人
|
|
|
|
|
+ remaining = Math.round(remaining * 100) / 100.0;
|
|
|
|
|
+ result.add(remaining);
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static void main(String[] args) throws InterruptedException {
|
|
|
|
|
+ double totalAmount = 188.0;
|
|
|
|
|
+ List<String> peopleCount = new ArrayList<>();
|
|
|
|
|
+ peopleCount.add("阿吉");
|
|
|
|
|
+ peopleCount.add(".");
|
|
|
|
|
+ peopleCount.add("打工人阿阿阿作");
|
|
|
|
|
+ peopleCount.add("哥布林斯莱耶");
|
|
|
|
|
+ peopleCount.add("阿吉一天不\uD83E\uDD8C积阳德二天不\uD83E\uDD8C积积阳阳德 ");
|
|
|
|
|
+ peopleCount.add("管理在此谁敢造次");
|
|
|
|
|
+ peopleCount.add("くろいまと");
|
|
|
|
|
+ peopleCount.add("长沙陈奕迅");
|
|
|
|
|
+ peopleCount.add("旧雨");
|
|
|
|
|
+ peopleCount.add("阿1");
|
|
|
|
|
+
|
|
|
|
|
+ //System.out.println("=== 方法一:整数分计算 ===");
|
|
|
|
|
+ List<Double> redPacket1 = generateRedPacket(totalAmount, peopleCount);
|
|
|
|
|
+ printResult(redPacket1, totalAmount, peopleCount);
|
|
|
|
|
+
|
|
|
|
|
+// System.out.println("\n=== 方法二:BigDecimal精确计算 ===");
|
|
|
|
|
+// List<BigDecimal> redPacket2 = generateRedPacketPrecise(new BigDecimal("188.00"), peopleCount);
|
|
|
|
|
+// printResult(redPacket2, new BigDecimal("188.00"), peopleCount);
|
|
|
|
|
+//
|
|
|
|
|
+// System.out.println("\n=== 方法三:简单的二倍均值法 ===");
|
|
|
|
|
+// List<Double> redPacket3 = simpleRedPacket(totalAmount, peopleCount);
|
|
|
|
|
+// printResult(redPacket3, totalAmount, peopleCount);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static void printResult(List<Double> amounts, double totalAmount,List<String> people) throws InterruptedException {
|
|
|
|
|
+ double sum = 0;
|
|
|
|
|
+ System.out.println("以下为2026年新年红包名单:");
|
|
|
|
|
+ for (int i = 0; i < people.size(); i++) {
|
|
|
|
|
+ Thread.sleep(1000);
|
|
|
|
|
+ System.out.print("*");
|
|
|
|
|
+ Thread.sleep(1000);
|
|
|
|
|
+ System.out.print("*");
|
|
|
|
|
+ Thread.sleep(1000);
|
|
|
|
|
+ System.out.print("*");
|
|
|
|
|
+ Thread.sleep(1000);
|
|
|
|
|
+ System.out.print("*");
|
|
|
|
|
+ Thread.sleep(1000);
|
|
|
|
|
+ System.out.print("*");
|
|
|
|
|
+ Thread.sleep(1000);
|
|
|
|
|
+ System.out.print("*");
|
|
|
|
|
+ Thread.sleep(2000);
|
|
|
|
|
+ System.out.println("");
|
|
|
|
|
+ String name = people.get(i)+": %.2f元\n";
|
|
|
|
|
+ System.out.printf(name, amounts.get(i));
|
|
|
|
|
+ sum += amounts.get(i);
|
|
|
|
|
+ Thread.sleep(2000);
|
|
|
|
|
+ }
|
|
|
|
|
+ System.out.printf("总计: %.2f元 (预期: %.2f元)\n", sum, totalAmount);
|
|
|
|
|
+ System.out.printf("最大金额: %.2f元, 最小金额: %.2f元\n",
|
|
|
|
|
+ amounts.stream().max(Double::compare).orElse(0.0),
|
|
|
|
|
+ amounts.stream().min(Double::compare).orElse(0.0));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static void printResult(List<BigDecimal> amounts, BigDecimal totalAmount,List<String> people) {
|
|
|
|
|
+ BigDecimal sum = BigDecimal.ZERO;
|
|
|
|
|
+ System.out.println("以下为2026年新年红包名单:");
|
|
|
|
|
+ for (int i = 0; i < people.size(); i++) {
|
|
|
|
|
+ String name = people.get(i)+": %.2f元\n";
|
|
|
|
|
+ System.out.printf(name, amounts.get(i));
|
|
|
|
|
+ sum = sum.add(amounts.get(i));
|
|
|
|
|
+ }
|
|
|
|
|
+ System.out.printf("总计: %.2f元 (预期: %.2f元)\n", sum, totalAmount);
|
|
|
|
|
+
|
|
|
|
|
+ BigDecimal max = amounts.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
|
|
|
|
|
+ BigDecimal min = amounts.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
|
|
|
|
|
+ System.out.printf("最大金额: %.2f元, 最小金额: %.2f元\n", max, min);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|