网站首页 > java教程 正文
随机红包算法,每个人都有自己的实现思路。
package com.jmmq.load.jim.algorithm;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
* 红包算法
*/
public class RedPacketPrc {
private static final BigDecimal MIN = new BigDecimal("0.01");
public static void main(String[] args) {
String[] rs = redPacketRandom(new BigDecimal(1), 100, false);
BigDecimal sum = BigDecimal.ZERO;
for(String str: rs){
System.out.println(str);
sum = sum.add(new BigDecimal(str));
}
System.out.println("sum:" + sum);
System.out.println("(ˉ`?._.? ?._.?′ˉ)(ˉ`???′ˉ) (ˉ`?._.? ?._.?′ˉ)(ˉ`???′ˉ) (ˉ`?._.? ?._.?′ˉ)(ˉ`???′ˉ)");
String[] res = redPacketOpen(new BigDecimal(20), 6, false);
BigDecimal sum2 = BigDecimal.ZERO;
for(String str: res){
System.out.println(str);
sum2 = sum2.add(new BigDecimal(str));
}
System.out.println("sum2:" + sum2);
}
/**
*
* @param faceValue 面值 单位:元
* @param amount 人数
* @param avgFlag 是否平均
* @return
* 平均分就不写了
* 思路一:
* 按百分比进行随机计算,最后一个进行匝差计算
*/
public static String[] redPacketRandom(BigDecimal faceValue, int amount, boolean avgFlag){
// 先计算平均数 四舍五入
BigDecimal perValue = faceValue.divide(new BigDecimal(amount),3, BigDecimal.ROUND_HALF_UP);
// 平均下来最小的红包必须大于等于 1分钱
if(perValue.compareTo(MIN) == -1){
throw new RuntimeException("red packet amount OutOfBounds min = 0.01");
}
String[] rs = new String[amount];
// 平均
if(avgFlag){
// 这里四舍五入重新计算
perValue = faceValue.divide(new BigDecimal(amount),2, BigDecimal.ROUND_HALF_UP);
Arrays.fill(rs, perValue.toString());
// 无法除尽
if(perValue.multiply(new BigDecimal(amount)).compareTo(faceValue) != 0 ){
// 随机获取一个下标
int index = new Random().nextInt(amount);
rs[index] = String.valueOf(new BigDecimal(rs[index]).add(
faceValue.subtract(perValue.multiply(new BigDecimal(amount)))));
}
return rs;
}
BigDecimal weightSum = BigDecimal.ZERO;
BigDecimal redSum = BigDecimal.ZERO;
BigDecimal[] weights = new BigDecimal[amount];
// 随机
for (int i=0; i<rs.length; i++){
BigDecimal seed = new BigDecimal(new Random().nextInt(amount));
weightSum = weightSum.add(seed);
weights[i] = seed;
// 循环最后一个后计算金额
if(i == rs.length -1){
for(int j=0; j<rs.length; j++){
// 修改为向下取值,这样差额永远都是正数就不存在吃不掉差额的问题了
BigDecimal val = faceValue.multiply(weights[j].divide(weightSum,2, BigDecimal.ROUND_HALF_UP));
if(val.compareTo(MIN) == -1){
val = MIN;
}
redSum = redSum.add(val);
rs[j] = val.toString();
}
}
}
BigDecimal diff = faceValue.subtract(redSum);
if(faceValue.compareTo(redSum) != 0){
// 找到第一个加上或减少diff大于MIN的元素修改金额
for(int i=0; i<rs.length; i++){
if(new BigDecimal(rs[i]).add(diff).compareTo(MIN) > -1 ){
rs[i] = new BigDecimal(rs[i]).add(diff).toString();
break;
}
}
}
// 若没有任何一个元素能吃掉差额,因此差额需要进行分摊了,
// 这里简单粗暴的将循环到大于MIN的都改为MIN同时减少差额
// 这里代码不进行优化,会写另外一个优化的算法
if(faceValue.compareTo(redSum) != 0){
for(int i=0; i<rs.length; i++){
if(new BigDecimal(rs[i]).compareTo(MIN) > 0){
if(new BigDecimal(rs[i]).add(diff).compareTo(MIN) > -1){
rs[i] = new BigDecimal(rs[i]).add(diff).toString();
break;
} else {
diff.add(new BigDecimal(rs[i])).subtract(MIN);
rs[i] = MIN.toString();
}
}
}
}
return rs;
}
/**
* 上面算法进行优化
* @param faceValue 面值 单位:元
* @param amount 人数
* @param avgFlag 是否平均
* @return
* 优化思路:
* 上面算法为了保证红包最小金额处理上花费了大量的循环来处理
* 优化上就直接先分配最小金额,然后在进行随机分摊剩余的钱
*/
public static String[] redPacketOpen(BigDecimal faceValue, int amount, boolean avgFlag){
// 先计算平均数 四舍五入
BigDecimal perValue = faceValue.divide(new BigDecimal(amount),3, BigDecimal.ROUND_HALF_UP);
// 平均下来最小的红包必须大于等于 1分钱
if(perValue.compareTo(MIN) == -1){
throw new RuntimeException("red packet amount OutOfBounds min = 0.01");
}
String[] rs = new String[amount];
// 平均
if(avgFlag){
// 这里四舍五入重新计算
perValue = faceValue.divide(new BigDecimal(amount),2, BigDecimal.ROUND_HALF_UP);
Arrays.fill(rs, perValue.toString());
// 无法除尽
if(perValue.multiply(new BigDecimal(amount)).compareTo(faceValue) != 0 ){
// 随机获取一个下标
int index = new Random().nextInt(amount);
rs[index] = String.valueOf(new BigDecimal(rs[index]).add(
faceValue.subtract(perValue.multiply(new BigDecimal(amount)))));
}
return rs;
}
// 随机
// 先把低保放进去
Arrays.fill(rs, MIN.toString());
BigDecimal weightSum = BigDecimal.ZERO;
BigDecimal redSum = BigDecimal.ZERO;
BigDecimal[] weights = new BigDecimal[amount];
// 随机
for (int i=0; i<rs.length; i++){
BigDecimal seed = new BigDecimal(new Random().nextInt(amount));
weightSum = weightSum.add(seed);
weights[i] = seed;
// 循环最后一个后计算金额
if(i == rs.length -1){
for(int j=0; j<rs.length; j++){
BigDecimal val = faceValue.multiply(weights[j].divide(weightSum,2, BigDecimal.ROUND_DOWN));
redSum = redSum.add(val);
rs[j] = val.add(MIN).toString();
}
}
}
// 随机获取一个下标
BigDecimal diff = faceValue.subtract(redSum);
int index = new Random().nextInt(amount);
rs[index] = String.valueOf(new BigDecimal(rs[index]).add(diff));
return rs;
}
}
猜你喜欢
- 2024-09-16 mysq时间进位问题(mysql 当前时间函数)
- 2024-09-16 Java程序员们请你不要用战术上的勤奋掩盖你自己站略上的懒惰
- 2024-09-16 Java中存储金额用什么数据类型?(java用什么数据类型表示金额)
- 2024-09-16 java 坐标类_java实现坐标(java中坐标)
- 2024-09-16 MySQL常用函数,程序员真得看看(mysql函数用法)
- 2024-09-16 Java中Double保留后小数位的几种方法
- 2024-09-16 Java面试官问我10道题,我答错了一半,你能答对多少?
- 2024-09-16 java.util.Math类--数学相关的工具类
- 2024-09-16 怎么让四舍五入都不亏?(怎么四舍五入到个位)
- 2024-09-16 一张图彻底搞懂Java取整函数ceil、floor、round的区别!建议收藏
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)