Java实现4种微信抢红包算法(小结)

2023-01-21 0 1,810

目录

概述

14年微信推出红包功能以后,很多公司开始上自己的红包功能,到现在为止仍然有很多红包开发的需求,实现抢红包算法也是面试常考题。

要求:

  • 保证每个红包最少分得0.01元
  • 保证每个红包金额概率尽量均衡
  • 所有红包累计金额登于红包总金额

本文提供4中红包算法及Java代码实现demo,仅供参考。其中每种算法测试场景为:0.1元10个包,1元10个包,100元10个包,1000元10个包。

一、剩余金额随机法

以10元10个红包为例,去除每个红包的最小金额后,红包剩余9.9元;

  • 第一个红包在[0,9.9]范围随机,假设随机得1元,则第一个红包金额为1.1元,红包剩余8.9元。
  • 第二个红包在[0,8.9]范围随机,假设随机得1.5元,则第二个红包金额为1.6元,红包剩余7.4元。
  • 第三个红包在[0,7.4]范围随机,假设随机得0.5元,则第三个红包金额为0.6元,红包剩余6.9元。
  • 以此类推。
public static void main(String[] args) {
    //初始化测试场景
    BigDecimal[][] rrr = {
            {new BigDecimal(\"0.1\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"1\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"100\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"1000\"), new BigDecimal(\"10\")}
    };
    BigDecimal min = new BigDecimal(\"0.01\");
    //测试个场景
    for (BigDecimal[] decimals : rrr) {
        final BigDecimal amount = decimals[0];
        final BigDecimal num = decimals[1];
        System.out.println(amount + \"元\" + num + \"个人抢=======================================================\");
        test1(amount, min, num);
    }
}

private static void test1(BigDecimal amount, BigDecimal min, BigDecimal num) {
    BigDecimal remain = amount.subtract(min.multiply(num));
    final Random random = new Random();
    final BigDecimal hundred = new BigDecimal(\"100\");
    BigDecimal sum = BigDecimal.ZERO;
    BigDecimal redpeck;
    for (int i = 0; i < num.intValue(); i++) {
        final int nextInt = random.nextInt(100);
        if (i == num.intValue() - 1) {
            redpeck = remain;
        } else {
            redpeck = new BigDecimal(nextInt).multiply(remain).divide(hundred, 2, RoundingMode.FLOOR);
        }
        if (remain.compareTo(redpeck) > 0) {
            remain = remain.subtract(redpeck);
        } else {
            remain = BigDecimal.ZERO;
        }
        sum = sum.add(min.add(redpeck));
        System.out.println(\"第\" + (i + 1) + \"个人抢到红包金额为:\" + min.add(redpeck));
    }
    System.out.println(\"校验每个红包累计额度是否等于红包总额结果:\" + (amount.compareTo(sum) == 0));
}

测试结果如下:可以看出此算法有明显缺陷,即:先领取的红包金额较大,后领取的红包金额较小,这就使得抢红包便的不公平。
0.1元10个人抢=======================================================
第1个人抢到红包金额为:0.01
第2个人抢到红包金额为:0.01
第3个人抢到红包金额为:0.01
第4个人抢到红包金额为:0.01
第5个人抢到红包金额为:0.01
第6个人抢到红包金额为:0.01
第7个人抢到红包金额为:0.01
第8个人抢到红包金额为:0.01
第9个人抢到红包金额为:0.01
第10个人抢到红包金额为:0.01
校验每个红包累计额度是否等于红包总额结果:true
1元10个人抢=======================================================
第1个人抢到红包金额为:0.09
第2个人抢到红包金额为:0.28
第3个人抢到红包金额为:0.19
第4个人抢到红包金额为:0.20
第5个人抢到红包金额为:0.15
第6个人抢到红包金额为:0.02
第7个人抢到红包金额为:0.03
第8个人抢到红包金额为:0.01
第9个人抢到红包金额为:0.01
第10个人抢到红包金额为:0.02
校验每个红包累计额度是否等于红包总额结果:true
100元10个人抢=======================================================
第1个人抢到红包金额为:19.99
第2个人抢到红包金额为:29.58
第3个人抢到红包金额为:38.27
第4个人抢到红包金额为:11.85
第5个人抢到红包金额为:0.11
第6个人抢到红包金额为:0.13
第7个人抢到红包金额为:0.01
第8个人抢到红包金额为:0.01
第9个人抢到红包金额为:0.03
第10个人抢到红包金额为:0.02
校验每个红包累计额度是否等于红包总额结果:true
1000元10个人抢=======================================================
第1个人抢到红包金额为:60.00
第2个人抢到红包金额为:695.54
第3个人抢到红包金额为:229.72
第4个人抢到红包金额为:8.95
第5个人抢到红包金额为:0.29
第6个人抢到红包金额为:4.64
第7个人抢到红包金额为:0.01
第8个人抢到红包金额为:0.69
第9个人抢到红包金额为:0.12
第10个人抢到红包金额为:0.04
校验每个红包累计额度是否等于红包总额结果:true

二、二倍均值法(微信红包采用此法)

还是以10元10个红包为例,去除每个红包的最小金额后,红包剩余9.9元,二倍均值计算公式:2 * 剩余金额/剩余红包数

  • 第一个红包在[0,1.98]范围随机,假设随机得1.9,则第一个红包金额为2.0,红包剩余8元。
  • 第二个红包在[0,2]范围随机,假设随机的1元,则第二个红包金额为1.1元,红包剩余7元。
  • 第三个红包在[0,2]范围随机,假设随机的0.5元,则第三个红包金额为0.6元,红包剩余5.5元。
  • 以此类推。
public static void main(String[] args) {
    //初始化测试场景
    BigDecimal[][] rrr = {
            {new BigDecimal(\"0.1\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"1\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"100\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"1000\"), new BigDecimal(\"10\")}
    };
    BigDecimal min = new BigDecimal(\"0.01\");
    //测试个场景
    for (BigDecimal[] decimals : rrr) {
        final BigDecimal amount = decimals[0];
        final BigDecimal num = decimals[1];
        System.out.println(amount + \"元\" + num + \"个人抢=======================================================\");
        test2(amount, min, num);
    }
}


private static void test2(BigDecimal amount,BigDecimal min ,BigDecimal num){
    BigDecimal remain = amount.subtract(min.multiply(num));
    final Random random = new Random();
    final BigDecimal hundred = new BigDecimal(\"100\");
    final BigDecimal two = new BigDecimal(\"2\");
    BigDecimal sum = BigDecimal.ZERO;
    BigDecimal redpeck;
    for (int i = 0; i < num.intValue(); i++) {
        final int nextInt = random.nextInt(100);
        if(i == num.intValue() -1){
            redpeck = remain;
        }else{
            redpeck = new BigDecimal(nextInt).multiply(remain.multiply(two).divide(num.subtract(new BigDecimal(i)),2,RoundingMode.CEILING)).divide(hundred,2, RoundingMode.FLOOR);
        }
        if(remain.compareTo(redpeck) > 0){
            remain = remain.subtract(redpeck);
        }else{
            remain = BigDecimal.ZERO;
        }
        sum = sum.add(min.add(redpeck));
        System.out.println(\"第\"+(i+1)+\"个人抢到红包金额为:\"+min.add(redpeck));
    }
    System.out.println(\"校验每个红包累计额度是否等于红包总额结果:\"+amount.compareTo(sum));
}

测试结果如下:此算法很好的保证了抢红包几率大致均等。
0.1元10个人抢=======================================================
第1个人抢到红包金额为:0.01
第2个人抢到红包金额为:0.01
第3个人抢到红包金额为:0.01
第4个人抢到红包金额为:0.01
第5个人抢到红包金额为:0.01
第6个人抢到红包金额为:0.01
第7个人抢到红包金额为:0.01
第8个人抢到红包金额为:0.01
第9个人抢到红包金额为:0.01
第10个人抢到红包金额为:0.01
校验每个红包累计额度是否等于红包总额结果:true
100元10个人抢=======================================================
第1个人抢到红包金额为:6.20
第2个人抢到红包金额为:7.09
第3个人抢到红包金额为:10.62
第4个人抢到红包金额为:18.68
第5个人抢到红包金额为:18.74
第6个人抢到红包金额为:2.32
第7个人抢到红包金额为:15.44
第8个人抢到红包金额为:5.43
第9个人抢到红包金额为:15.16
第10个人抢到红包金额为:0.32
校验每个红包累计额度是否等于红包总额结果:true
1元10个人抢=======================================================
第1个人抢到红包金额为:0.08
第2个人抢到红包金额为:0.05
第3个人抢到红包金额为:0.17
第4个人抢到红包金额为:0.17
第5个人抢到红包金额为:0.08
第6个人抢到红包金额为:0.06
第7个人抢到红包金额为:0.18
第8个人抢到红包金额为:0.10
第9个人抢到红包金额为:0.02
第10个人抢到红包金额为:0.09
校验每个红包累计额度是否等于红包总额结果:true
1000元10个人抢=======================================================
第1个人抢到红包金额为:125.99
第2个人抢到红包金额为:165.08
第3个人抢到红包金额为:31.90
第4个人抢到红包金额为:94.78
第5个人抢到红包金额为:137.79
第6个人抢到红包金额为:88.89
第7个人抢到红包金额为:156.44
第8个人抢到红包金额为:7.97
第9个人抢到红包金额为:151.01
第10个人抢到红包金额为:40.15
校验每个红包累计额度是否等于红包总额结果:true

三、整体随机法

还是以10元10个红包为例,随机10个数,红包金额公式为:红包总额 * 随机数/随机数总和,假设10个随机数为[5,9,8,7,6,5,4,3,2,1],10个随机数总和为50,

  • 第一个红包10*5/50,得1元。
  • 第二个红包10*9/50,得1.8元。
  • 第三个红包10*8/50,得1.6元。
  • 以此类推。
public static void main(String[] args) {
    //初始化测试场景
    BigDecimal[][] rrr = {
            {new BigDecimal(\"0.1\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"1\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"100\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"1000\"), new BigDecimal(\"10\")}
    };
    BigDecimal min = new BigDecimal(\"0.01\");
    //测试个场景
    for (BigDecimal[] decimals : rrr) {
        final BigDecimal amount = decimals[0];
        final BigDecimal num = decimals[1];
        System.out.println(amount + \"元\" + num + \"个人抢=======================================================\");
        test3(amount, min, num);
    }
}

private static void test3(BigDecimal amount,BigDecimal min ,BigDecimal num){
    final Random random = new Random();
    final int[] rand = new int[num.intValue()];
    BigDecimal sum1 = BigDecimal.ZERO;
    BigDecimal redpeck ;
    int sum = 0;
    for (int i = 0; i < num.intValue(); i++) {
        rand[i] = random.nextInt(100);
        sum += rand[i];
    }
    final BigDecimal bigDecimal = new BigDecimal(sum);
    BigDecimal remain = amount.subtract(min.multiply(num));
    for (int i = 0; i < rand.length; i++) {
        if(i == num.intValue() -1){
            redpeck = remain;
        }else{
            redpeck = remain.multiply(new BigDecimal(rand[i])).divide(bigDecimal,2,RoundingMode.FLOOR);
        }
        if(remain.compareTo(redpeck) > 0){
            remain = remain.subtract(redpeck);
        }else{
            remain = BigDecimal.ZERO;
        }
        sum1= sum1.add(min.add(redpeck));
        System.out.println(\"第\"+(i+1)+\"个人抢到红包金额为:\"+min.add(redpeck));
    }

    System.out.println(\"校验每个红包累计额度是否等于红包总额结果:\"+(amount.compareTo(sum1)==0));
}

测试结果如下:此算法随机性较大。
0.1元10个人抢=======================================================
第1个人抢到红包金额为:0.01
第2个人抢到红包金额为:0.01
第3个人抢到红包金额为:0.01
第4个人抢到红包金额为:0.01
第5个人抢到红包金额为:0.01
第6个人抢到红包金额为:0.01
第7个人抢到红包金额为:0.01
第8个人抢到红包金额为:0.01
第9个人抢到红包金额为:0.01
第10个人抢到红包金额为:0.01
校验每个红包累计额度是否等于红包总额结果:true
100元10个人抢=======================================================
第1个人抢到红包金额为:2.35
第2个人抢到红包金额为:14.12
第3个人抢到红包金额为:5.74
第4个人抢到红包金额为:6.61
第5个人抢到红包金额为:0.65
第6个人抢到红包金额为:10.97
第7个人抢到红包金额为:9.15
第8个人抢到红包金额为:7.93
第9个人抢到红包金额为:1.31
第10个人抢到红包金额为:41.17
校验每个红包累计额度是否等于红包总额结果:true
1元10个人抢=======================================================
第1个人抢到红包金额为:0.10
第2个人抢到红包金额为:0.02
第3个人抢到红包金额为:0.12
第4个人抢到红包金额为:0.03
第5个人抢到红包金额为:0.05
第6个人抢到红包金额为:0.12
第7个人抢到红包金额为:0.06
第8个人抢到红包金额为:0.01
第9个人抢到红包金额为:0.04
第10个人抢到红包金额为:0.45
校验每个红包累计额度是否等于红包总额结果:true
1000元10个人抢=======================================================
第1个人抢到红包金额为:148.96
第2个人抢到红包金额为:116.57
第3个人抢到红包金额为:80.49
第4个人抢到红包金额为:32.48
第5个人抢到红包金额为:89.39
第6个人抢到红包金额为:65.60
第7个人抢到红包金额为:20.77
第8个人抢到红包金额为:16.03
第9个人抢到红包金额为:36.79
第10个人抢到红包金额为:392.92
校验每个红包累计额度是否等于红包总额结果:true

四、割线

还是以10元10个红包为例,在(0,10)范围随机9个间隔大于等于0.01数,假设为[1,1.2,2,3,4,5,6,7,8]

  • 第一个红包得1元
  • 第二个红包得0.2元
  • 第三个红得0.8元。
  • 以此类推。
public static void main(String[] args) {
    //初始化测试场景
    BigDecimal[][] rrr = {
            {new BigDecimal(\"0.1\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"1\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"100\"), new BigDecimal(\"10\")},
            {new BigDecimal(\"1000\"), new BigDecimal(\"10\")}
    };
    BigDecimal min = new BigDecimal(\"0.01\");
    //测试个场景
    for (BigDecimal[] decimals : rrr) {
        final BigDecimal amount = decimals[0];
        final BigDecimal num = decimals[1];
        System.out.println(amount + \"元\" + num + \"个人抢=======================================================\");
        test3(amount, min, num);
    }
}

private static void test3(BigDecimal amount,BigDecimal min ,BigDecimal num){
    final Random random = new Random();
    final int[] rand = new int[num.intValue()];
    BigDecimal sum1 = BigDecimal.ZERO;
    BigDecimal redpeck ;
    int sum = 0;
    for (int i = 0; i < num.intValue(); i++) {
        rand[i] = random.nextInt(100);
        sum += rand[i];
    }
    final BigDecimal bigDecimal = new BigDecimal(sum);
    BigDecimal remain = amount.subtract(min.multiply(num));
    for (int i = 0; i < rand.length; i++) {
        if(i == num.intValue() -1){
            redpeck = remain;
        }else{
            redpeck = remain.multiply(new BigDecimal(rand[i])).divide(bigDecimal,2,RoundingMode.FLOOR);
        }
        if(remain.compareTo(redpeck) > 0){
            remain = remain.subtract(redpeck);
        }else{
            remain = BigDecimal.ZERO;
        }
        sum1= sum1.add(min.add(redpeck));
        System.out.println(\"第\"+(i+1)+\"个人抢到红包金额为:\"+min.add(redpeck));
    }

    System.out.println(\"校验每个红包累计额度是否等于红包总额结果:\"+(amount.compareTo(sum1)==0));
}

测试结果如下:此算法随机性较大,且性能不好
0.1元10个人抢=======================================================
第1个人抢到红包金额为:0.01
第2个人抢到红包金额为:0.01
第3个人抢到红包金额为:0.01
第4个人抢到红包金额为:0.01
第5个人抢到红包金额为:0.01
第6个人抢到红包金额为:0.01
第7个人抢到红包金额为:0.01
第8个人抢到红包金额为:0.01
第9个人抢到红包金额为:0.01
第10个人抢到红包金额为:0.01
校验每个红包累计额度是否等于红包总额结果:true
100元10个人抢=======================================================
第1个人抢到红包金额为:19.84
第2个人抢到红包金额为:2.73
第3个人抢到红包金额为:8.95
第4个人抢到红包金额为:14.10
第5个人抢到红包金额为:18.60
第6个人抢到红包金额为:3.66
第7个人抢到红包金额为:9.17
第8个人抢到红包金额为:15.49
第9个人抢到红包金额为:5.61
第10个人抢到红包金额为:1.85
校验每个红包累计额度是否等于红包总额结果:true
1元10个人抢=======================================================
第1个人抢到红包金额为:0.02
第2个人抢到红包金额为:0.28
第3个人抢到红包金额为:0.03
第4个人抢到红包金额为:0.02
第5个人抢到红包金额为:0.11
第6个人抢到红包金额为:0.23
第7个人抢到红包金额为:0.18
第8个人抢到红包金额为:0.09
第9个人抢到红包金额为:0.03
第10个人抢到红包金额为:0.01
校验每个红包累计额度是否等于红包总额结果:true
1000元10个人抢=======================================================
第1个人抢到红包金额为:69.28
第2个人抢到红包金额为:14.68
第3个人抢到红包金额为:373.16
第4个人抢到红包金额为:274.73
第5个人抢到红包金额为:30.77
第6个人抢到红包金额为:30.76
第7个人抢到红包金额为:95.55
第8个人抢到红包金额为:85.20
第9个人抢到红包金额为:10.44
第10个人抢到红包金额为:15.43
校验每个红包累计额度是否等于红包总额结果:true

:本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可, 转载请附上原文出处链接。
1、本站提供的源码不保证资源的完整性以及安全性,不附带任何技术服务!
2、本站提供的模板、软件工具等其他资源,均不包含技术服务,请大家谅解!
3、本站提供的资源仅供下载者参考学习,请勿用于任何商业用途,请24小时内删除!
4、如需商用,请购买正版,由于未及时购买正版发生的侵权行为,与本站无关。
5、本站部分资源存放于百度网盘或其他网盘中,请提前注册好百度网盘账号,下载安装百度网盘客户端或其他网盘客户端进行下载;
6、本站部分资源文件是经压缩后的,请下载后安装解压软件,推荐使用WinRAR和7-Zip解压软件。
7、如果本站提供的资源侵犯到了您的权益,请邮件联系: 442469558@qq.com 进行处理!

猪小侠源码-最新源码下载平台 Java教程 Java实现4种微信抢红包算法(小结) http://www.20zxx.cn/463210/xuexijiaocheng/javajc.html

猪小侠源码,优质资源分享网

常见问题
  • 本站所有资源版权均属于原作者所有,均只能用于参考学习,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担
查看详情
  • 最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,建议提前注册好百度网盘账号,使用百度网盘客户端下载
查看详情

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务