看完微信抢红包算法你就明白,为啥你不是手气最佳

2023-05-16

摘要:今天我们就来分析一下抢红包的算法,其中有一些是微信红包的算法,看完你就知道手气最佳是如何产生的啦。

本文分享自华为云社区《为啥春节抢红包总不是手气最佳?看完微信抢红包算法你就明白了!》,作者: XiaoLin_Java。

前言

春节必不可少的活动就是抢红包啦,从以前的纸质红包到现在互联网红包(以微信红包为首),今天我们就来分析一下抢红包的算法,其中有一些是微信红包的算法,看完你就知道手气最佳是如何产生的啦!

算法一:剩余金额随机法

算法一是不推荐使用的,算法一全称叫剩余金额随机法,听名字就知道这个方法是将剩余的金额进行随机分配,我们先来看代码。

// 分配红包的算法
private static void testPocket(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).setScale(2, BigDecimal.ROUND_HALF_UP));
}
System.out.println("红包总额:" + sum.setScale(2, BigDecimal.ROUND_HALF_UP));
}
// 测试代码
public static void main(String[] args) {
    BigDecimal amount = new BigDecimal(100).setScale(2, BigDecimal.ROUND_HALF_UP);
    BigDecimal min = new BigDecimal(0.01).setScale(2, BigDecimal.ROUND_HALF_UP);
    BigDecimal num = new BigDecimal(10).setScale(2, BigDecimal.ROUND_HALF_UP);
    testPocket2(amount,min,num);
}

 

我们可以看到,这个方法是有很明显的缺陷的,就是一开始领到红包的人获取的金额可能是最大的,后面领取的金额就逐渐变小了,因为他是从剩余额金额进行随机的。很显然微信是肯定不会使用这种方法作为红包瓜分算法,不然每次一有红包,马上领取就有可能获取手气最佳,但是明显不是。

算法二:整体随机法

整体金额随机法的公式:红包总额 * 随机数/随机数总和,这个方法的核心是使用一个随机数作为红包瓜分的标准,这个随机数是通过Random类随机产生的。他的随机性就比较大了,看起来好像是和我们平时抢红包差不多,但是微信红包也不是采用这种方法,因为这种的随机性太大了,不是很公平。

private static void testPocket2(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)).setScale(2, BigDecimal.ROUND_HALF_UP);
        System.out.println("第"+(i+1)+"个人抢到红包金额为:"+min.add(redpeck).setScale(2, BigDecimal.ROUND_HALF_UP));
    }

    System.out.println("红包总额:"+sum1);
}

// 测试代码
public static void main(String[] args) {
    BigDecimal amount = new BigDecimal(100).setScale(2, BigDecimal.ROUND_HALF_UP);
    BigDecimal min = new BigDecimal(0.01).setScale(2, BigDecimal.ROUND_HALF_UP);
    BigDecimal num = new BigDecimal(10).setScale(2, BigDecimal.ROUND_HALF_UP);
    testPocket2(amount,min,num);
}

他的随机性可谓是很高,也不是最佳选择。

算法三:割线法

割线法指的是把红包总金额想象成一条很长的线段,而每个人抢到的金额,则是这条主线段所拆分出的若干子线段,当所有切割点确定以后,子线段的长度也随之确定。这样每个人来抢红包的时候,只需要顺次领取与子线段长度等价的红包金额即可。

private static void testPocket3(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).setScale(2, BigDecimal.ROUND_HALF_UP);
        } else {
            remain = BigDecimal.ZERO;
        }
        sum1 = sum1.add(min.add(redpeck).setScale(2, BigDecimal.ROUND_HALF_UP));
        System.out.println("第" + (i + 1) + "个人抢到红包金额为:" + min.add(redpeck));
    }

    System.out.println("红包总额:" + sum1);
}
// 测试代码
public static void main(String[] args) {
    BigDecimal amount = new BigDecimal(100).setScale(2, BigDecimal.ROUND_HALF_UP);
    BigDecimal min = new BigDecimal(0.01).setScale(2, BigDecimal.ROUND_HALF_UP);
    BigDecimal num = new BigDecimal(10).setScale(2, BigDecimal.ROUND_HALF_UP);
    testPocket2(amount,min,num);
}

他的随机性也比较大,但是他最致命的是性能,因为他需要进行切割这个步骤。

算法四:二倍均值法

算法四就是微信红包目前所采用的的算法(大致思路,代码模拟),二倍均值计算公式:2 * 剩余金额/剩余红包数。

  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).setScale(2, BigDecimal.ROUND_HALF_UP);
        }else{
            remain = BigDecimal.ZERO;
        }
        sum = sum.add(min.add(redpeck)).setScale(2, BigDecimal.ROUND_HALF_UP);
        System.out.println("第"+(i+1)+"个人抢到红包金额为:"+min.add(redpeck));
    }
    System.out.println("红包总额:" + sum);
}

 

他还是比较好的保证了每个红包金额大致相等,不会出现极端情况。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

看完微信抢红包算法你就明白,为啥你不是手气最佳 的相关文章

  • Android之NFC

    NFC简介 xff1a Near Field Communication 近场通信 xff0c 是一种数据传输技术 与wifi 蓝牙 红外线等数据传输技术的一个主要差异就是有效距离一般不能超过4cm NFC支持3种工作模式 xff1a 1
  • 修改netbeans字体显示的终极方法(适用于任何版本)

    netbeans字体难看的原因 xff1a netbeans代码编辑器和输出窗口使用monospaced字体来显示 xff0c 而monospaced代表着等宽字体 xff0c 应该是显示程序源代码的合理选择 monospaced是种逻辑字
  • SSH框架代码(struts1.2+spring2.0+hibernate3.2)第二部分

    续上一篇 业务逻辑段结构 xff1a applicationContext common xml lt xml version 61 34 1 0 34 encoding 61 34 UTF 8 34 gt lt Application c
  • 导出目录下所有文件的命令

    可以导出文件创建时间 xff0c 和名称 dir s gt gt d list txt
  • crontab使用详解

    名称 crontab 使用权限 所有使用者 使用方式 crontab u user file crontab u user l r e 说明 crontab 是用来让使用者在固定时间或固定间隔执行程序之用 xff0c 换句话说 xff0c
  • 养成自己的好习惯

    一 积极思维的好习惯 事物本身并不影响人 xff0c 人们只受到自己对事物看法的影响 xff0c 人必须改变被动的思维习惯 xff0c 养成积极的思维习惯 当你在实现目标的过程中 xff0c 面对具体的工作和任务时 xff0c 你的大脑里去
  • 迁移学习技巧+网络关键字比预训练模型关键字多前缀

    一 修改预训练模型中的全连接层参数 xff1a 方式1 xff1a 修改字典的方式 import torch import torch nn as nn import torchvision class ResNet nn Module d
  • kali下使用远程桌面连接

    apt span class token operator span get install rdesktop rdesktop IP 端口
  • vscode sftp 插件配置

    插件名称 SFTP ctrl 43 shift 43 p SFTP Config 配置命令 34 name 34 34 名称 34 34 host 34 34 34 地址 34 34 protocol 34 34 ftp 34 34 por
  • linux系统上升级centos6.8内核

    linux系统上升级centos6 8内核 1 查看默认版本 uname r 2 更新nss yum update nss 3 安装elrepo的yum源 xff0c 升级内核需要使用elrepo的yum源 xff0c 在安装yum源之前还
  • Spring-为什么需要使用依赖注入

    为什么要使用依赖注入 xff1f 在我们了解为什么要使用依赖注入之前 xff0c 我们需要了解什么是依赖注入 依赖注入是什么 xff1f 在Spring中有一个很重要的设计思想 IOC xff08 Inversion of Control
  • STM32外部晶振不起振的原因

    1 确保STM32的使用外部时钟的配置正确 xff0c 然后将程序烧写到单片机 xff0c 一定记住空单片机的外部晶振不能起振的 2 怀疑是否负载电容过大了 xff0c 因布线原因可能的杂散电容使得就有一定的负载电容 方法 xff1a 更换
  • rt-thread SPI配置流程

    参考 基于 RT Thread Studio 的 SPI 驱动开发文档 RT Thread 文档中心 SPI 设备 RT Thread 文档中心 1 打开SPI设备驱动框架 配置结果如图 2 定义SPI总线相关的宏 比如使用了SPI2 在
  • RT-THREAD STM32 UART配置后,串口RX悬空一直接收到数据

    nbsp nbsp nbsp nbsp 根据RT THREAD 串口配置步骤完成后 串口RX悬空一直收到数据 因为RX引脚配置为浮空输入了 且RX有没有上下拉电阻 导致干扰出现 使得总是接收到杂乱数据 将RX引脚改为上拉后 问题解决 也就是
  • 使用rt-thread studio配置STM32F103RET6的多串口流程

    一 配置串口 1 进入RT Thread Setting将serial模块打开 2 一般都使用DMA模式 继续进入serial 打开DMA模式 保存 3 进入board h文件 进行使用的串口宏配置 UART CONFIG BEGIN Af
  • 如何取消Ubuntu的密码登录,实现自动登录

    在虚拟机上安装了Ubuntu xff0c 切换到虚拟机的时候 xff0c 总是需要输入密码 xff0c 以下方法可实现取消密码登录 xff0c 实现自动登录 1 打开右上角的Setting 2 点击Users gt 点击Unlock 3 输
  • QT UI如何实现自适应布局

    1 拖至少两个控件到窗口中 2 选中窗体 xff0c 然后右键 gt 布局 gt 选布局类型即可 3 设置窗体布局比例 xff0c 修改layoutStretch属性 layoutRowStretch
  • QT中的wait、wakeOne、wakeAll的使用注意

    QWaitCondition允许线程在一定条件下唤醒其他线程 其中wakeOne 函数在条件满足时随机唤醒一个等待线程 xff0c 而wakeAll 函数则在条件满足时唤醒所有等待线程 1 bool wait QMutex mutex un
  • QT C++的容器类存储自定义对象的操作

    在各种容器中存放的类型 xff0c 必须有默认的构造函数 xff0c 拷贝构造函数和赋值操作 由于QObject及所有继承自它的子类都没有提供拷贝构造和赋值操作 xff0c 当我们使用QList时 xff0c 编译器就会报错 测试对象 xf
  • QT的connect函数的第五个参数:Qt::ConnectionType

    一 QT的connect 第5个参数一般不填 xff0c 为默认值 enum ConnectionType AutoConnection DirectConnection QueuedConnection BlockingQueuedCon

随机推荐