发表于 2021-4-7 19:26

申请会员ID:whmynb【申请通过】

1、申 请 I D :whmynb
2、个人邮箱:503268460@qq.com

前言
之前思考一个问题:微信的手气红包是如何实现的
怎么保证了所有人 无先后都有机会成为手气最佳

初次尝试
一开始的想法很简单,就是固定一个奖金池,因为要保证每个最低是有一分钱,所以把随机数的总金额减去剩下每人的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);
    }

结果为:
https://img-blog.csdnimg.cn/20190125154248190.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyMTcxNDg=,size_16,color_FFFFFF,t_70
发现使用这种算法,第一次进来的人都是拿的红包大概率都是最大的,而最后抢到的大概率是最小的,而且从平均数可以看到第一个可以拿走奖金池中一半的红包,就算是按照此方法先分出红包存入数组,再让别人随机顺序领也不行,因为微信上没有出现过多人红包可以取走一半的情况(分的少不算啊)
第二次尝试
痛定思痛,因为第一次尝试发现自己想的过于简单了,当抢红包需要规定抢到的红包最大范围,我看到网上有一种是平均数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);
    }
}
测试结果,感觉好像达到了自己想要的效果,赶紧循环个几次试试看,看看次序的平均红包金额吧
https://img-blog.csdnimg.cn/20190125154337273.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyMTcxNDg=,size_16,color_FFFFFF,t_70
准备多套了一个方法,为了可以偷懒复用之前的代码
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次的时候,金额几乎相差无几https://img-blog.csdnimg.cn/20190125154443653.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyMTcxNDg=,size_16,color_FFFFFF,t_70
小结
通过上面的代码,已经实现了微信抢红包的基本逻辑,可能和微信真正的红包方法完全不一样,这里只是写出了一个基础部分代码,给大家提供了一个小思路而已

Hmily 发表于 2021-4-9 10:11

I D:whmynb
邮箱:503268460@qq.com

申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。

whmynb 发表于 2021-4-9 16:04

报到报到,谢谢H大:victory:
页: [1]
查看完整版本: 申请会员ID:whmynb【申请通过】