Java实现四种微信抢红包算法,拿走不谢

概述

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

获取途径:点击领取学习资料

要求:

  1. 保证每个红包最少分得0.01元
  2. 保证每个红包金额概率尽量均衡
  3. 所有红包累计金额等于红包总金额

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

一、剩余金额随机法

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

  1. 第一个红包在[0,9.9]范围随机,假设随机得1元,则第一个红包金额为1.1元,红包剩余8.9元。
  2. 第二个红包在[0,8.9]范围随机,假设随机得1.5元,则第二个红包金额为1.6元,红包剩余7.4元。
  3. 第三个红包在[0,7.4]范围随机,假设随机得0.5元,则第三个红包金额为0.6元,红包剩余6.9元。
  4. 以此类推。
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 * 剩余金额/剩余红包数

  1. 第一个红包在[0,1.98]范围随机,假设随机得1.9,则第一个红包金额为2.0,红包剩余8元。
  2. 第二个红包在[0,2]范围随机,假设随机的1元,则第二个红包金额为1.1元,红包剩余7元。
  3. 第三个红包在[0,2]范围随机,假设随机的0.5元,则第三个红包金额为0.6元,红包剩余5.5元。
  4. 以此类推。
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,

  1. 第一个红包10*5/50,得1元。
  2. 第二个红包10*9/50,得1.8元。
  3. 第三个红包10*8/50,得1.6元。
  4. 以此类推。
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. 第一个红包得1元
  2. 第二个红包得0.2元
  3. 第三个红得0.8元。
  4. 以此类推。
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 

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
微信抢红包算法涉及到微信支付和微信红包接口的调用,需要进行授权认证和支付操作,因此需要使用微信官方提供的SDK进行开发。 以下是一个基本的微信抢红包算法的示例代码: ```php <?php require_once "WxPayApi.php"; // 引入微信支付SDK // 微信支付配置参数 $config = new WxPayConfig(); $config->SetAppId("YOUR_APPID"); $config->SetMchId("YOUR_MCHID"); $config->SetKey("YOUR_KEY"); $config->SetNotifyUrl("YOUR_NOTIFY_URL"); // 获取红包信息 $redpack = $_POST["redpack"]; // 前端传递的红包信息 $redpack_info = json_decode($redpack, true); // 解析红包信息 // 构造红包支付请求参数 $input = new WxPayUnifiedOrder(); $input->SetBody("红包支付"); // 商品描述 $input->SetOut_trade_no($redpack_info["redpack_id"]); // 商户订单号 $input->SetTotal_fee($redpack_info["total_amount"] * 100); // 总金额,单位为分 $input->SetSpbill_create_ip($_SERVER["REMOTE_ADDR"]); // 终端IP $input->SetNotify_url($config->GetNotifyUrl()); // 通知地址 $input->SetTrade_type("JSAPI"); // 交易类型 $input->SetOpenid($redpack_info["openid"]); // 用户标识 // 调用统一下单API获取预支付交易会话标识 $wxpay_api = new WxPayApi($config); $result = $wxpay_api->unifiedOrder($input); if ($result["return_code"] == "SUCCESS" && $result["result_code"] == "SUCCESS") { // 构造红包接口请求参数 $redpack_input = new WxPayRedPack(); $redpack_input->SetMch_billno($redpack_info["redpack_id"]); // 商户订单号 $redpack_input->SetSend_name("微信红包"); // 商户名称 $redpack_input->SetRe_openid($redpack_info["openid"]); // 用户openid $redpack_input->SetTotal_amount($redpack_info["total_amount"] * 100); // 付款金额,单位为分 $redpack_input->SetTotal_num(1); // 红包发放总人数 $redpack_input->SetWishing("恭喜发财,大吉大利"); // 红包祝福语 $redpack_input->SetClient_ip($_SERVER["REMOTE_ADDR"]); // Ip地址 $redpack_input->SetAct_name("微信红包"); // 活动名称 $redpack_input->SetRemark("微信红包"); // 备注信息 // 调用红包接口发放红包 $result = $wxpay_api->sendRedPack($redpack_input); if ($result["return_code"] == "SUCCESS" && $result["result_code"] == "SUCCESS") { echo "红包发送成功!"; } else { echo "红包发送失败!"; } } else { echo "统一下单失败!"; } ``` 需要注意的是,该示例代码仅供参考,实际开发中需要根据具体业务场景进行适当的修改和调整。另外,微信红包接口需要在微信商户平台进行开通和配置,具体操作请参考微信官方文档和开发者文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值