Java微信手气红包实现

Java微信手气红包实现

前言

昨天坐公交车,微信群里在发红包,突然想到在思考一个问题:微信的手气红包的算法是如何实现的

闲的没事开始尝试起来了

初次尝试

一开始的想法很简单,就是固定一个奖金池,因为要保证每个最低是有一分钱,所以把随机数的总金额减去剩下每人的0.01作为最高可以获取的红包金额,接着把奖金池减去刚生成的金额,当到最后一人时,直接去把剩余的金额全部给他

具体代码如下(金额的单位为分):

public static List<Integer> redPackRand(Integer money,Integer peopleCount){
        List<Integer> result=new ArrayList<Integer>();
        for(int i=0;i<peopleCount;i++){
            if(i==peopleCount-1){//判断是不是最后一个人,是的话直接把所有金额全部给他
//                System.out.println("第"+(i+1)+"人,获得:"+money);
                result.add(money);
            }else {
                //取得随机的金额
                Integer getMoney =(int)((money-(peopleCount-i-1))*Math.random()+1);
//                System.out.println("第"+(i+1)+"人,获得:"+getMoney);
                //奖金池中减去取走的部分
                money-=getMoney;
//                System.out.println("当前剩余:"+money);
                result.add(getMoney);
            }
        }
        return result;
    }

测试


为了测试出红包算法的情况,我写了一个测试工具,可以循环测试N次,每次M个人,查看第1-M个人可以抢到多少钱的平均数

//分别保存进来顺序信息的创建对象
static class PackeAvg{
    	//保存所有金额数据
        List<Integer> list;
        public PackeAvg(){
            list=new ArrayList<>();
        }
		//为给列表添加金额
        public void addList(Integer a){
            list.add(a);
        }
        public List<Integer> getList() {
            return list;
        }
		//算出平均值
        public Integer getAvg() {
            if(list.size()!=0){
                int sum=0;
                for (int num:list){
                    sum+=num;
                }
                return sum / list.size();
            }
            return 0;
        }
    }

public static void redPackRandAvg(int forCount){
   		//用于保存次序信息的
        List<PackeAvg> avglist=new ArrayList<>();
    	//循环次数forCount
        for(int i =0;i<forCount;i++){
            //300块钱10个人抢,单位是分
            List<Integer> list = redPackRand(30000, 10);
            //标记是第几个人
            int j=0;
            for (int num:list){
                if(avglist.size()<j+1)avglist.add(new PackeAvg());
                PackeAvg packeAvg = avglist.get(j);
                packeAvg.addList(num);
                j++;
            }
        }
        int i=1;
        for (PackeAvg avg:avglist){
            System.out.println("第"+i+"个人的平均红包为:"+avg.getAvg()+",具体为"+new Gson().toJson(avg));
            i++;
        }
    }
    public static void main(String[] arg){
        //调用20000次,看看评价情况
        redPackRandAvg(20000);
    }

结果为:
在这里插入图片描述

发现使用这种算法,第一次进来的人都是拿的红包大概率都是最大的,而最后抢到的大概率是最小的,而且从平均数可以看到第一个可以拿走奖金池中一半的红包,就算是按照此方法先分出红包存入数组,再让别人随机顺序领也不行,因为微信上没有出现过多人红包可以取走一半的情况(分的少不算啊)

第二次尝试

痛定思痛,因为第一次尝试发现自己想的过于简单了,当抢红包需要规定抢到的红包最大范围,我看到网上有一种是平均数x2的方法,就是100块10个人抢,第一个人可以抢到的是0.01~20块钱之间,而当红包剩下40人数还剩5人的话,下一个人可以拿到的是2*(40/5),就是0.01~16块钱,这样就可以保证红包不会一下子都被拿走,也能控制红包金额大小

为了方便,我这次专门写了一个类

public class RedPackeg {
    //为总金额
    private Integer money;
    //剩余金额
    private Integer nowMoney;
    //红包分了多少份
    private Integer packSize;
    //取走了多少份
    private Integer packNum;
    //保存金额信息
	private List<Integer> moneyList=new ArrayList<>();
    public List<Integer> getMoneyList() {
        return moneyList;
    }
	//构造函数,初始化金额和红包份数
    public RedPackeg(Integer money,Integer packSize){
        this.money=money;
        this.nowMoney=money;
        this.packSize=packSize;
        this.packNum=0;

    }
	//取红包,当取完之后返回-1
    public Integer getRedPack(){
        if(packSize>packNum){
            Integer getMoney=(int)(((nowMoney/(packSize-packNum))*2)*Math.random()+1);
            //如果getMoney取走后,剩下的钱不够每人0.01的,就把除去没人0.01外的钱都给他
            if((nowMoney-getMoney)<(packSize-packNum))getMoney = nowMoney-(packSize+packNum);
            packNum++;
            nowMoney-=getMoney;
            //如果是最后一个人就把金额全部给他
            if(packSize==packNum)getMoney+=nowMoney;
            moneyList.add(getMoney);
            return getMoney;
        }
        return -1;
    }
    
    //测试单次抢红包,能不能保证把红包金额全部发完
    //第一次忘记考虑最后一人情况,金额并没有全发完
    public static void main(String[] pp){
        RedPackeg packeg=new RedPackeg(10000,10);
        for (int i=0;i<10;i++){
            System.out.println("第"+(i+1)+"个人,获得了"+packeg.getRedPack());
        }
        List<Integer> list = packeg.getMoneyList();
        int sum=0;
        for(int a:list){
            sum+=a;
        }
        System.out.println("红包总数:"+sum);
    }
}

测试结果,感觉好像达到了自己想要的效果,赶紧循环个几次试试看,看看次序的平均红包金额吧
在这里插入图片描述

准备多套了一个方法,为了可以偷懒复用之前的代码

public static List<Integer> redPackGet(Integer money,Integer peopleCount){
        RedPackeg packeg=new RedPackeg(money,peopleCount);
    	//从里面取10次钱,记录都会保存在list中
        for (int i=0;i<peopleCount;i++)packeg.getRedPack();
        return packeg.getMoneyList();
    }
public static void redPackAvg(int forCount){
        List<PackeAvg> avglist=new ArrayList<>();
        for(int i =0;i<forCount;i++){
            //只是改变了这里的一个方法
            List<Integer> list = redPackGet(30000, 10);
            int j=0;
            for (int num:list){
                if(avglist.size()<j+1)avglist.add(new PackeAvg());
                PackeAvg packeAvg = avglist.get(j);
                packeAvg.addList(num);
                j++;
            }
        }
        int i=1;
        for (PackeAvg avg:avglist){
            System.out.println("第"+i+"个人的平均红包为:"+avg.getAvg()+",具体为"+new Gson().toJson(avg));
            i++;
        }
    }
    public static void main(String[] arg){
        redPackAvg(20000);
    }

结果:
在这里插入图片描述

循环了20000次,结果非常的喜人,基本上可以保证不管是第几个抢红包平均的金额都是差不多的

当循环次数到400000次的时候,金额几乎相差无几
在这里插入图片描述

小结

通过上面的代码,已经实现了微信抢红包的基本逻辑,但是真实项目中还有各种问题,就比如高并发就需要对剩余金额加锁之类的,这里只是写出了一个基础部分代码

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是Java实现微信手气红包算法的代码: ```java import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Random; public class RedPacketUtil { /** * 拆分红包 * * @param totalAmount 红包总金额(单位:元) * @param totalPeople 红包总人数 * @return 拆分后的红包金额列表 */ public static List<BigDecimal> splitRedPacket(BigDecimal totalAmount, int totalPeople) { List<BigDecimal> amountList = new ArrayList<>(); Random random = new Random(); BigDecimal leftAmount = totalAmount; int leftPeople = totalPeople; for (int i = 0; i < totalPeople - 1; i++) { // 随机生成一个红包金额,范围为:0.01元 ~ 剩余平均值的两倍 BigDecimal amount = BigDecimal.valueOf(random.nextDouble()) .multiply(leftAmount.divide(BigDecimal.valueOf(leftPeople), 2, BigDecimal.ROUND_HALF_UP)) .multiply(BigDecimal.valueOf(2)) .setScale(2, BigDecimal.ROUND_HALF_UP); amountList.add(amount); leftAmount = leftAmount.subtract(amount); leftPeople--; } amountList.add(leftAmount); return amountList; } /** * 计算红包总金额 * * @param amountList 红包金额列表 * @return 红包总金额(单位:元) */ public static BigDecimal getTotalAmount(List<BigDecimal> amountList) { BigDecimal totalAmount = BigDecimal.ZERO; for (BigDecimal amount : amountList) { totalAmount = totalAmount.add(amount); } return totalAmount; } public static void main(String[] args) { BigDecimal totalAmount = BigDecimal.valueOf(10); // 红包总金额为10元 int totalPeople = 5; // 拆分红包的总人数为5人 List<BigDecimal> amountList = splitRedPacket(totalAmount, totalPeople); System.out.println(amountList); } } ``` 使用示例: ```java BigDecimal totalAmount = BigDecimal.valueOf(10); // 红包总金额为10元 int totalPeople = 5; // 拆分红包的总人数为5人 List<BigDecimal> amountList = RedPacketUtil.splitRedPacket(totalAmount, totalPeople); // 拆分红包 BigDecimal total = RedPacketUtil.getTotalAmount(amountList); // 计算红包总金额 ``` 实现思路: 1. 先定义一个红包总金额和拆分红包的总人数; 2. 初始化一个空的红包金额列表和一个Random对象; 3. 循环拆分红包,每次生成一个随机金额,范围为:0.01元 ~ 剩余平均值的两倍; 4. 将生成的红包金额添加到列表中,并更新剩余金额和剩余人数; 5. 最后将剩余金额作为最后一个红包金额添加到列表中; 6. 计算红包总金额,返回红包金额列表。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值