本文目录导读:

二倍均值法(常用算法)
这是微信红包使用的经典算法,保证每个人都能分到钱且相对公平。
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class RedPacketUtils {
/**
* 二倍均值法发红包
* @param totalAmount 总金额(元)
* @param totalNum 红包个数
* @return 每个红包的金额列表
*/
public static List<Double> splitRedPacket(double totalAmount, int totalNum) {
List<Double> amountList = new ArrayList<>();
// 将元转换为分,避免浮点数精度问题
int totalCent = (int) (totalAmount * 100);
int remainAmount = totalCent;
int remainNum = totalNum;
Random random = new Random();
for (int i = 0; i < totalNum - 1; i++) {
// 每个人获得的红包金额是 [0.01, 剩余平均值*2] 之间的随机数
int maxAmount = remainAmount / remainNum * 2;
int amount = random.nextInt(maxAmount) + 1; // 至少1分
remainAmount -= amount;
remainNum--;
amountList.add(amount / 100.0);
}
// 最后一个人获得剩余金额
amountList.add(remainAmount / 100.0);
return amountList;
}
public static void main(String[] args) {
double totalAmount = 10.00;
int totalNum = 5;
List<Double> packets = splitRedPacket(totalAmount, totalNum);
System.out.println("红包分配结果:");
double sum = 0;
for (int i = 0; i < packets.size(); i++) {
System.out.printf("第%d个红包:%.2f元%n", i+1, packets.get(i));
sum += packets.get(i);
}
System.out.printf("总金额:%.2f元%n", sum);
}
}
完整版红包服务
包含接口定义、实体类和业务逻辑:
// 红包结果实体类
public class RedPacketResult {
private Long userId;
private Double amount;
private Long timestamp;
// 构造器、getter、setter省略
}
// 红包服务接口
public interface RedPacketService {
/**
* 发红包
*/
RedPacket sendRedPacket(Long userId, double totalAmount, int totalNum);
/**
* 抢红包
*/
RedPacketResult grabRedPacket(Long redPacketId, Long userId);
}
// 红包服务实现
@Service
public class RedPacketServiceImpl implements RedPacketService {
private static final double MIN_AMOUNT = 0.01;
// 模拟存储
private Map<Long, RedPacket> redPacketMap = new ConcurrentHashMap<>();
private AtomicLong idGenerator = new AtomicLong(1);
@Override
public RedPacket sendRedPacket(Long userId, double totalAmount, int totalNum) {
// 参数校验
if (totalAmount < totalNum * MIN_AMOUNT) {
throw new IllegalArgumentException("金额不足以分配");
}
RedPacket redPacket = new RedPacket();
redPacket.setId(idGenerator.getAndIncrement());
redPacket.setUserId(userId);
redPacket.setTotalAmount(totalAmount);
redPacket.setTotalNum(totalNum);
redPacket.setRemainAmount(totalAmount);
redPacket.setRemainNum(totalNum);
redPacket.setStatus(RedPacketStatus.ACTIVE);
redPacket.setCreateTime(new Date());
// 预分配红包金额(二倍均值法)
redPacket.setAmountList(splitRedPacket(totalAmount, totalNum));
redPacketMap.put(redPacket.getId(), redPacket);
return redPacket;
}
@Override
public RedPacketResult grabRedPacket(Long redPacketId, Long userId) {
RedPacket redPacket = redPacketMap.get(redPacketId);
if (redPacket == null) {
throw new RuntimeException("红包不存在");
}
synchronized (redPacket.getId().toString().intern()) {
if (redPacket.getRemainNum() <= 0) {
throw new RuntimeException("红包已抢完");
}
// 获取当前用户应得的金额
int index = redPacket.getTotalNum() - redPacket.getRemainNum();
Double amount = redPacket.getAmountList().get(index);
// 更新剩余信息
redPacket.setRemainAmount(redPacket.getRemainAmount() - amount);
redPacket.setRemainNum(redPacket.getRemainNum() - 1);
RedPacketResult result = new RedPacketResult();
result.setUserId(userId);
result.setAmount(amount);
result.setTimestamp(System.currentTimeMillis());
return result;
}
}
private List<Double> splitRedPacket(double totalAmount, int totalNum) {
// 使用之前的二倍均值法
return RedPacketUtils.splitRedPacket(totalAmount, totalNum);
}
}
高并发抢红包方案
使用Redis实现高并发红包系统:
@Service
public class RedisRedPacketService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String RED_PACKET_KEY = "red_packet:";
private static final String RED_PACKET_CONSUME_KEY = "red_packet:consume:";
/**
* 发红包(Redis实现)
*/
public String sendRedPacket(double totalAmount, int totalNum) {
// 生成红包ID
String redPacketId = UUID.randomUUID().toString().replace("-", "");
// 将金额转换为分存储
int totalCent = (int) (totalAmount * 100);
// 预生成红包金额列表
List<Integer> amountList = new ArrayList<>();
int remainAmount = totalCent;
int remainNum = totalNum;
Random random = new Random();
for (int i = 0; i < totalNum - 1; i++) {
int max = remainAmount / remainNum * 2;
int amount = random.nextInt(max) + 1;
amountList.add(amount);
remainAmount -= amount;
remainNum--;
}
amountList.add(remainAmount);
// 将红包金额列表打乱后存入Redis List
Collections.shuffle(amountList);
String key = RED_PACKET_KEY + redPacketId;
for (Integer amount : amountList) {
redisTemplate.opsForList().rightPush(key, amount);
}
// 设置过期时间(24小时)
redisTemplate.expire(key, 24, TimeUnit.HOURS);
return redPacketId;
}
/**
* 抢红包(Redis实现,支持高并发)
*/
public Double grabRedPacket(String redPacketId, Long userId) {
String key = RED_PACKET_KEY + redPacketId;
String consumeKey = RED_PACKET_CONSUME_KEY + redPacketId;
// 使用Lua脚本保证原子性
String luaScript =
"local amount = redis.call('lpop', KEYS[1]) " +
"if amount then " +
" redis.call('hset', KEYS[2], ARGV[1], amount) " +
" return amount " +
"end " +
"return nil";
Long amount = redisTemplate.execute(
(RedisCallback<Long>) connection -> {
byte[][] keys = new byte[][]{
key.getBytes(),
consumeKey.getBytes()
};
byte[][] argv = new byte[][]{
userId.toString().getBytes()
};
return (Long) connection.eval(
luaScript.getBytes(),
ReturnType.INTEGER,
2,
keys,
argv
);
}
);
if (amount == null) {
return null;
}
return amount / 100.0;
}
}
简单测试类
public class RedPacketTest {
public static void main(String[] args) {
// 测试二倍均值法
System.out.println("===== 测试红包分配 =====");
double total = 100;
int num = 10;
List<Double> amounts = RedPacketUtils.splitRedPacket(total, num);
double sum = 0;
for (int i = 0; i < amounts.size(); i++) {
System.out.printf("用户%d: %.2f元%n", i+1, amounts.get(i));
sum += amounts.get(i);
}
System.out.printf("合计: %.2f元%n", sum);
// 多次测试看分布
System.out.println("\n===== 多次测试统计 =====");
for (int j = 0; j < 5; j++) {
amounts = RedPacketUtils.splitRedPacket(total, num);
double max = amounts.stream().mapToDouble(Double::doubleValue).max().getAsDouble();
double min = amounts.stream().mapToDouble(Double::doubleValue).min().getAsDouble();
System.out.printf("第%d次 - 最大:%.2f, 最小:%.2f%n", j+1, max, min);
}
}
}
算法特点说明
-
二倍均值法:
- 公平性:每人抢到的金额随机
- 期望值:每个人获得金额的期望值相同
- 不会出现金额为0的情况
-
注意事项:
- 使用分作为单位避免浮点数精度问题
- 高并发时需考虑线程安全
- 实际生产环境建议使用Redis等中间件
-
扩展功能:
- 可以增加红包过期自动退款
- 可以记录抢红包日志
- 可以实现红包查看记录功能
这个实现可以满足大多数红包业务场景的需求。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播