微信红包是怎么实现的

微信红包的实现方式是怎么样的?

  微信红包的思路:

    每个人当前抢到的微信红包金额大小服从:区间[0.01,2*当前剩余红包均值两倍)上的均匀分布。可能不太好理解,举个例子:某个时刻你抢到了红包,在你抢红包前红包余额为m,红包剩余个数为n,那么你抢到的金额一定是在[0.01,2*m/n)区间内,其中m/n就是你抢红包前每个人可以均分到的红包金额,即剩余红包均值。

  微信红包算法应该主要考虑以下几个因素:
    1.微信红包金额是随机数:只不过这个随机数有限制:精确到两位小数;最小金额是0.01;最大金额也有限制:2倍的剩余红包平均金额.
    2.不能超发,就是你发了3块钱红包最后红包总额不能超过3块。
    3.先抢和后抢的收益均值要大致相同
  基于上面几点,博主写了一份示例代码,如下:(代码地址:https://github.com/LoveWK/JavaBase.git):/src/com/wk/WeChatRedPackage/
  1 /**
  2  * 〈一句话功能简述〉<br> 
  3  * 〈微信红包的实现原理〉
  4  *
  5  * @author wangkai_wb
  6  * @create 2020/6/9
  7  * @since 1.0.0
  8  * 微信红包算法应该主要考虑以下几个因素:
  9  * 1.微信红包金额是随机数
 10  *  只不过这个随机数有限制:精确到两位小数;最小金额是0.01;
 11  *  最大金额也有限制:2倍的剩余红包平均金额(2倍的数据是毕导视频给出的)
 12  * 2.不能超发,就是你发了3块钱红包最后红包总额不能超过3块。
 13  * 3.先抢和后抢的收益均值要大致相同
 14  */
 15 public class WeChatRedPackage {
 16     public static void main(String[] args) {
 17         //设置红包总金额参数
 18         double sum = 0;
 19         //定义Map集合,用于获取每个人对应的金额
 20         Map<String,Double> map = new HashMap<>();
 21         //定义一个list集合,用于获取运气王
 22         List list = new ArrayList();
 23         //获得红包分配的数组集合
 24         ArrayList<Double> res = WXRedPackageAlgorithm(10,3);
 25         //遍历集合
 26         for (int i = 0; i <res.size() ; i++) {
 27             double money = res.get(i);
 28             sum += money;
 29             System.out.println("第"+i+"个人获得:"+money+"元");
 30             //将每个人获取的值传到map中
 31             map.put("第"+i+"个人",money);
 32         }
 33         System.out.println("红包已被领完");
 34         System.out.println();
 35         System.out.println("红包总金额为:"+sum+"元");
 36         //按升序排序
 37         Map<String,Double> sortedMap =sortByValue(map);
 38         sortedMap.forEach((key, value)->{
 39 //            System.out.println(key+":"+value);
 40             list.add(key);
 41         });
 42         System.out.println();
 43         System.out.println("恭喜"+list.get(0)+"成为运气王,获得金额:"+sortedMap.get(list.get(0))+"元");
 44     }
 45 
 46     /**
 47      * 红包分配的数组集合
 48      * @param restMoney 红包总额
 49      * @param restNum 分给的人数
 50      * @return
 51      */
 52     private static ArrayList<Double> WXRedPackageAlgorithm(double restMoney,int restNum){
 53         //根据需要分配的人数进行红包总金额的分配
 54         ArrayList<Double> res = new ArrayList<>(restNum);
 55         // 生成随机数工具
 56         Random random = new Random();
 57 
 58         while (restNum >1){
 59             // 最大的红包为:两倍的平均红包大小
 60             double max = (restMoney/restNum)*2;
 61             // 产生[0,1)之间的随机数
 62             double r = random.nextDouble();
 63             // 抢到的红包区间[0,max)
 64             double money = r*max;
 65             if(money<0.01)
 66                 money = 0.01;
 67             else//floor方法:返回最大的(最接近正无穷大)double 值,该值小于等于参数,并等于某个整数。
 68                 money = Math.floor(money*100)/100;
 69             res.add(money);
 70 
 71             restNum--;
 72             //剩余总金额 = 总金额-已经获取红包的金额
 73             restMoney -= money;
 74         }
 75         //最后一个红包为剩余余额
 76         res.add(Math.floor(restMoney*100)/100);
 77         return res;
 78     }
 79 
 80     /**
 81      * 对map集合进行降序排序,为了获得运气王
 82      * @param map
 83      * @param <K>
 84      * @param <V>
 85      * @return
 86      */
 87     private static <K,V extends Comparable<? super V>> Map<K,V> sortByValue(Map<K,V> map){
 88         //创建一个链表集合
 89         List<Map.Entry<K,V>> list = new LinkedList<>(map.entrySet());
 90         //使用集合类的排序方法
 91         Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
 92             @Override
 93             public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
 94                 //按降序排序,如果不加前面的-号,就是升序排序
 95                 return -(o1.getValue().compareTo(o2.getValue()));
 96             }
 97         });
 98         //遍历list,分离出map,使用另一个map集合接收拍好序的集合
 99         Map<K,V> result = new LinkedHashMap<>();
100         for (Map.Entry<K,V> entry : list){
101             result.put(entry.getKey(),entry.getValue());
102         }
103         return result;
104     }
105 }

运行结果:

   上面有个问题是double类型运算时精度不足会导致,红包总金额为10,红包数量为3时,上面给出的代码跑出的结果可能最后用户只领到了9.99,解决这个问题可以采用java的BigDecimal类来解决精度问题。对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数的操作,有兴趣的同学可以试试改进下上面代码。

原文地址:https://www.cnblogs.com/wk-missQ1/p/13074343.html