网上那些乱七八糟的真是太多了 又不能运行 写的和描述的结果都不一致
决定自己写一个~
代码可以直接运行的
喜欢的话麻烦点个三连~~~
package com.superbrain.envelope.algorithm;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* <p> Description:抢红包</p >
* <p> CreationTime: 2021/4/10 16:18
*
* @author wangguowen
* @since 1.0
*/
@Data
@Builder
public class RedEnvelope implements Serializable {
private static final long serialVersionUID = -704116947856423131L;
/**
* 是否公平--固定金额/随机金额
*/
private boolean fair;
/**
* 红包总金额(元)
*/
private BigDecimal totalAccount;
/**
* 红包总数量
*/
private int totalNum;
}
package com.superbrain.envelope.service;
import com.superbrain.envelope.algorithm.RedEnvelope;
import java.math.BigDecimal;
import java.util.List;
/**
* <p> Description:RedEnvelopeService</p >
* <p> CreationTime: 2021/4/11 12:03
* <br>Copyright: ©2021 < a href=" ">Thunisoft</ a>
* <br>Email: < a href="mailto:wangguowen@thunisoft.com">wangguowen@thunisoft.com</ a></p >
*
* @author wangguowen
* @since 1.0
*/
public interface RedEnvelopeService {
/**
* 返回红包数组
*
* @param redEnvelope 红包对象
* @param userId 用户id
* @return 预分配好的红包集合
*/
List<BigDecimal> grabRedEnvelope(RedEnvelope redEnvelope, int userId);
}
package com.superbrain.envelope.service.impl;
import com.superbrain.envelope.model.RedEnvelope;
import com.superbrain.envelope.service.RedEnvelopeService;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
/**
* <p> Description:RedEnvelopeServiceImpl</p >
* <p> CreationTime: 2021/4/11 12:04
*
* @author wangguowen
* @since 1.0
*/
@Service
@Slf4j
public class RedEnvelopeServiceImpl implements RedEnvelopeService {
/**
* 最小分配的值
*/
@Value("${red.envelope.account.min}")
private String minAccount;
//
// @Value("${red.envelope.account.max}")
// private String maxAccount;
private static Random random = new Random();
static {
random.setSeed(System.currentTimeMillis());
}
static double sqrt(double n) {
// 对n开根号
return Math.sqrt(n);
}
static double sqr(double n) {
// 计算平方
return n * n;
}
static double nextRandom(double n) {
return random.nextDouble() * n;
}
/**
* 生产min和max之间的随机数,但是概率不是平均的,从min到max方向概率逐渐加大。
* 先平方,然后产生一个平方值范围内的随机数,再开方,这样就产生了一种“膨胀”再“收缩”的效果。
*
* @param min
* @param max
* @return
*/
static double xRandom(double min, double max) {
return sqrt(nextRandom(sqr(max - min)));
}
/**
* 返回红包集合
*
* @param redEnvelope 红包对象
* @return 预分配好的红包集合
*/
@Override
public List<BigDecimal> grabRedEnvelope(RedEnvelope redEnvelope) {
if (accountValid(redEnvelope)) {
return Lists.newArrayList();
}
List<BigDecimal> redEnvelopeList = redEnvelope.isFair() ?
//金额固定
fairAccountList(redEnvelope.getTotalAccount(), redEnvelope.getTotalNum()) :
//金额随机
randomAccountList(redEnvelope.getTotalAccount(), redEnvelope.getTotalNum());
//打乱顺序
Collections.shuffle(redEnvelopeList);
return redEnvelopeList;
}
/**
* 数额固定的红包分配机制
*
* @param redEnvelopeAccount 红包总金额
* @param redEnvelopeNum 红包数量
* @return 红包集合
*/
private List<BigDecimal> fairAccountList(BigDecimal redEnvelopeAccount, int redEnvelopeNum) {
List<BigDecimal> res = new ArrayList<>(redEnvelopeNum);
//平均数
BigDecimal avg = redEnvelopeAccount.divide(new BigDecimal(redEnvelopeNum), 2, RoundingMode.HALF_UP);
while (redEnvelopeNum > 1) {
//集合中添加固定金额
// BigDecimal money = avg;
res.add(avg);
redEnvelopeNum--;
redEnvelopeAccount = redEnvelopeAccount.subtract(avg);
}
//最后一个红包为剩余余额
res.add(redEnvelopeAccount);
return res;
}
/**
* 数额随机的红包分配机制
*
* @param redEnvelopeAccount 红包总金额
* @param redEnvelopeNum 红包数量
* @return 红包集合
*/
private List<BigDecimal> randomAccountList(BigDecimal redEnvelopeAccount, int redEnvelopeNum) {
List<BigDecimal> res = new ArrayList<>(redEnvelopeNum);
while (redEnvelopeNum > 1) {
//最大的红包为 (平均红包大小)*2
BigDecimal avg = redEnvelopeAccount.divide(new BigDecimal(redEnvelopeNum), 2, RoundingMode.DOWN);
BigDecimal avgMax = avg.multiply(new BigDecimal(2));
//产生[0,1)之间的随机数
double randomNum = random.nextDouble();
//抢到的红包区间[0,avgMax)
double doubleMoney = avgMax.doubleValue() * randomNum;
//保留两位小数
BigDecimal money = new BigDecimal(doubleMoney).setScale(2, RoundingMode.DOWN);
//保证最小值是minAccount
if (minAccount != null && money.compareTo(new BigDecimal(minAccount)) < 0) {
money = new BigDecimal(minAccount);
}
// //如果超过了最大值
// if (maxAccount != null && money.compareTo(new BigDecimal(maxAccount)) > 0) {
// double xRandom = xRandom(new BigDecimal(minAccount).doubleValue(), new BigDecimal(maxAccount).doubleValue());
// money = new BigDecimal(xRandom).add(avg);
// }
res.add(money);
//减红包数量 减红包金额
redEnvelopeNum--;
redEnvelopeAccount = redEnvelopeAccount.subtract(money);
}
log.info("最后一个红包是:" + redEnvelopeAccount);
//最后一个红包为剩余余额
res.add(redEnvelopeAccount);
return res;
}
private boolean accountValid(RedEnvelope redEnvelope) {
if (redEnvelope.getTotalAccount().compareTo(new BigDecimal(0)) <= 0) {
log.error("总金额必须大于0");
return true;
}
if (redEnvelope.getTotalNum() <= 0) {
log.error("总数量必须大于0");
return true;
}
// BigDecimal bigDecimal = redEnvelope.getTotalAccount().divide(new BigDecimal(redEnvelope.getTotalNum()),
// 2, RoundingMode.DOWN);
if (minAccount != null && (new BigDecimal(redEnvelope.getTotalNum()).multiply(new BigDecimal(minAccount))).compareTo(redEnvelope.getTotalAccount()) > 0) {
log.error("您必须保证每个红包至少" + minAccount + "元");
return true;
}
// if ((new BigDecimal(maxAccount).subtract(new BigDecimal(minAccount))).compareTo(new BigDecimal("1")) < 0) {
// log.error("您必须保证最大值和最小值之间相差1元");
// return true;
// }
return false;
}
}
package com.superbrain.envelope;
import com.superbrain.envelope.algorithm.RedEnvelope;
import com.superbrain.envelope.service.RedEnvelopeService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
@SpringBootTest
@Slf4j
class EnvelopeApplicationTests {
@Autowired
private RedEnvelopeService redEnvelopeService;
@Test
void grabRedEnvelopeRandom() {
RedEnvelope redEnvelope = RedEnvelope.builder()
.fair(false)
.totalAccount(new BigDecimal(100))
.totalNum(0)
.build();
List<BigDecimal> result = redEnvelopeService.grabRedEnvelope(redEnvelope, 1);
if (CollectionUtils.isEmpty(result)) {
return;
}
for (int i = 0; i < result.size(); i++) {
log.info("第" + (i + 1) + "个人分配到的红包额度是:" + result.get(i));
}
BigDecimal reduce = result.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
log.info("" + reduce);
}
@Test
void grabRedEnvelopeFair() {
RedEnvelope redEnvelope = RedEnvelope.builder()
.fair(true)
.totalAccount(new BigDecimal(100))
.totalNum(0)
.build();
List<BigDecimal> result = redEnvelopeService.grabRedEnvelope(redEnvelope, 1);
if (CollectionUtils.isEmpty(result)) {
return;
}
for (int i = 0; i < result.size(); i++) {
log.info("第" + (i + 1) + "个人分配到的红包额度是:" + result.get(i));
}
BigDecimal reduce = result.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
log.info("" + reduce);
}
}