随机红包算法,每个人都有自己的实现思路。
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;
}
}