二倍均值随机算法之抢拼手气红包场景应用

news2024/10/7 18:27:34

拼手气类的游戏,更能激发用户购物和社交的趣味性,以及游戏竞争心理,拼手气类的活动甚至可以影响人们消费心理。
拼手气红包就是最简单的例子,哪怕你手气红包只有0.01元,在众多竞争者中脱颖而出,抢到的那一刻还是很开心的。对于没有抢到的参与者,还会不服气,想要弥补自己的遗憾,从而更积极地参与活动。

两种生成红包的方式对比

拼手气红包有两种计算方法,一种是预计算,一种是实时算。

预计算在生成拼手气红包的时候,会提前根据总金额M和红包人数N,生成每个红包的金额,在抢红包的过程中,只需要从金额列表中取出,直至取完。这种方式需要占用额外的存储空间并且增加额外的I/O,在高并发场景下并不是最优的解决方案。
实时算采用的是纯内存计算,不需要预算空间存储,实时性很高。

蒙特卡罗方法

蒙特卡罗方法是由著名科学家、“计算机科学之父”和“博弈论之父”冯·诺依曼提出的,此种方法又可称为统计模拟法、随机抽样技术,是一种以概率和统计理论方法为基础的计算方法。主要是通过将待求解的问题采用相同的概率模型进行求解,并用电子计算机实现统计模拟或抽样,以获得问题的近似解。

蒙特卡罗方法生成随机数的主要步骤如下:
(1)针对实际问题建立一个简单且便于实现的概率统计模型,使所求的量恰好是该模型的概率分布或数字特征。
(2)基于模型的随机变量建立抽样方法,在计算机上进行模拟测试,抽取足够多的随机数。 (3)对模拟实验结果进行统计分析,给出所求解的估计值,这就是最终产生的随机数。
(4)必要时,可以通过改进模型以提高估计精度和减少实验费用,最终提高模拟效率。 直白点讲,这种算法主要是通过建立一个模型,并对模型中的随机变量建立抽样方法,在计算机中反复多次进行模拟测试,最终得到一个或多个估计值,即随机数列表。

二倍均值法

蒙特卡罗方法的核心思想主要在于构造一个数学模型,并基于若干个随机变量和统计分析的方法求出若干个估计值(随机数),在某种程度上并不适用于抢红包系统生成随机金额的几率相等、随机金额之和等于总金额等要求。 然而我们却可以借助其构造数学模型的思想,将总金额M和总个数N作为模型变量,从而求得一组红包随机金额。

当用户发出固定总金额为M、红包个数为N的红包后,由于系统后端采用的是预生成方式,因而将在后端生成N个随机金额的小红包并存储至缓存中,等待着被抢。 小红包随机金额的产生对用户而言是透明的,用户也无须知晓红包随机金额的产生原理与规则, 因而对于抢红包系统来说,只需要保证系统后端每次对于总金额M和红包个数N生成的小红包金额是随机且平等的,即间接性地保证每个用户抢到的红包金额是随机产生且概率是平等的即可。

除了保证预生成红包金额的随机性和概率平等性之外,抢红包系统后端还需保证3点要求:
(1)应保证所有人抢到的红包金额之和等于总金额M,这一点是毋庸置疑的。
(2)每个参与抢红包的用户,当抢到红包(即红包金额不为Null)时,金额应至少是1分钱,即0.01元。
(3)应当保证参与抢红包的用户抢到红包金额时,几率是相等的,这一点由于是采用预生成的方式,因而可以交给随机数生成算法进行控制。

目前市面上关于红包随机金额的生成算法有许多种,二倍均值法属于其中比较典型的一种。

二倍均值算法可以避免随机生成的金额,部分人金额过高,导致剩余的人的金额过小的尴尬。

代码实现与测试

顾名思义,二倍均值算法的核心思想是根据每次剩余的总金额M和剩余人数N,执行M/N再乘以2的操作得到一个边界值E,然后制定一个从0到E的随机区间,在这个随机区间内将产生一个随机金额R, 此时总金额M将更新为M-R,剩余人数N更新为N-1。再继续重复上述执行流程,以此类推,直至最终剩余人数N-1为0,即代表随机数已经产生完毕。

该算法的执行流程其实是一个for循环产生数据的过程,循环终止的条件是N-1<=0,即代表随机数已经生成完毕。 而对于随机数的生成,主要是在约束的随机区间(0,M/N×2)中产生,这样着实可以保证每次数值R产生的随机性和平等性; 除此之外,由于每次总金额M的更新采用的是递减的方式,即M=M-R,因而可以保证最终产生的所有随机金额之和等于M。

二倍均值法的核心执行逻辑在于不断地更新总金额M和剩余人数N,并根据M和N组成一个随机区间, 最终在这个区间内产生一个随机金额,如此不断地进行循环迭代,直至N-1为0,此时剩余的金额即为最后一个随机金额。

在这里插入图片描述

代码实现:

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executors;

/**
 * 二倍均值法的代码实战
 * -封装成工具类
 *
 * @author
 */
public class RedPacketUtil {

    /**
     * 发红包算法,金额参数以分为单位
     
     * @param totalAmount    红包总金额-单位为分
     * @param totalPeopleNum 总人数
     * @return
     */
    public static List<Integer> divideRedPackage(Integer totalAmount, Integer totalPeopleNum) {
        //用于存储每次产生的小红包随机金额List –金额单位为分
        List<Integer> amountList = new ArrayList<Integer>();
        //判断总金额和总个数参数的合法性
        if (totalAmount > 0 && totalPeopleNum > 0) {
            //记录剩余的总金额-初始化时金额即为红包的总金额
            Integer restAmount = totalAmount;
            //记录剩余的总人数-初始化时即为指定的总人数
            Integer restPeopleNum = totalPeopleNum;
            //定义产生随机数的实例对象
            Random random = new Random();
            //不断循环遍历、迭代更新地产生随机金额,直到N-1>0
            for (int i = 0; i < totalPeopleNum - 1; i++) {
                //随机范围:[1,剩余人均金额的两倍),左闭右开 -amount即为产生的随机金额R -单位为分
                int amount = random.nextInt(restAmount / restPeopleNum * 2 - 1) + 1;
                //更新剩余的总金额 M = M - R
                restAmount -= amount;
                //更新剩余的总人数N = N - 1
                restPeopleNum--;
                //将产生的随机金额添加进列表List中
                amountList.add(amount);
            }
            //循环完毕,剩余的金额即为最后一个随机金额,也需要将其添加进列表中
            amountList.add(restAmount);
        }
        //将最终产生的随机金额列表返回
        return amountList;
    }

    public static void divideRedPackageTester() {
        //总金额单位为分,在这里假设总金额为1000分,即10元
        Integer amount = new Random().nextInt(20000) + 1;
        if (amount < 10) {
            amount += 10;
        }
        //总人数即红包总个数,在这里假设为10个
        Integer total = new Random().nextInt(10) + 1;
        //调用二倍均值法工具类中产生随机金额列表的方法得到小红包随机金额列表
        List<Integer> list = RedPacketUtil.divideRedPackage(amount, total);
        System.out.println(String.format("%s总金额=%d分,即%s元,总个数=%d个",Thread.currentThread().getName(),
                amount, new BigDecimal(amount.toString()).divide(new BigDecimal("100")), total));
        //用于统计生成的随机金额之和是否等于总金额
        Integer sum = 0;
        //遍历输出每个随机金额
        for (Integer i : list) {
            //输出随机金额时包括单位为分和单位为元的信息
            //String s = String.format("随机金额为:%d分,即%s元 ", i, new BigDecimal(i.toString()).divide(new BigDecimal("100")));
            //System.out.println(s);
            sum += i;
        }
        System.out.println(String.format("%s所有随机金额叠加之和=%d分,即%s元", Thread.currentThread().getName(),sum, new BigDecimal(sum.toString()).divide(new BigDecimal("100"))));
        if (!sum.equals(amount)) {
            System.out.println(String.format("%s!!!!!!!!fail!!!!!!!!",Thread.currentThread().getName()));
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                divideRedPackageTester();
            }).start();
        }
    }

}

参考资料

  • 《分布式中间件技术实战:Java版》
  • 微信红包金额分配的算法 https://timyang.net/architecture/wechat-red-packet/
  • 微信红包的架构设计简介 https://www.zybuluo.com/yulin718/note/93148

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/362787.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Elasticsearch7.8.0版本进阶——多文档操作流程

目录一、多文档操作1.1、多文档操作的概述1.2、多文档操作与单文档模式区别二、用单个 mget 请求取回多个文档2.1、用单个 mget 请求取回多个文档的图解2.2、用单个 mget 请求取回多个文档的步骤三、bulk API 的模式请求取回多个文档3.1、bulk API 的模式请求取回多个文档的图解…

优炫数据库百城巡展,成都首站圆满举行

2月17日&#xff0c;由四川省大数据发展研究会、北京优炫软件股份有限公司联合举办的“首届四川省推进信息技术应用创新产业服务研讨会暨优炫数据库百城巡展成都首站隆重举行。此次活动是优炫数据库百城巡展的起点站&#xff0c;更是国产数据库市场美好乐章的一次强力鸣奏。 来…

HarmonyOS Connect认证测试

在HarmonyOS Connect生态产品的认证测试过程中&#xff0c;你是否存在这些疑问&#xff1a;认证流程具体包括哪些操作环节&#xff1f;如何根据实际场景选择合适的认证方式&#xff1f;如何选择认证测试标准的版本…… 本期FAQ为大家带来HarmonyOS Connect认证测试的常见问题…

ChatGPT类工具如何实现「降维打击」| 聊天机器人闭门研讨观点总结

导读随着ChatGPT出现&#xff0c;语言大模型的进步与对话交互方式相结合&#xff0c;正在搅动科研、产业&#xff0c;以及普通人的想象力。我们对智能的探索是正在步入决胜之局&#xff0c;还是仍在中场酣战&#xff1b;是需要精巧完备的一致系统&#xff0c;还是可以遵循实效至…

什么是网站ICP备案,什么样的网站需要办理ICP?

进入互联网时代&#xff0c;企业业务的展开或多或少都要接触网络&#xff0c;甚至也会在线上建立公司的业务平台。很多企业会选择建立一个网站来直接展现公司面貌和业务。在我们完成了搭建好网站的全部内容后&#xff0c;最重要的一步&#xff1a;在网站建成进入到上线展现时&a…

【无标题】10.货币系统

题目描述: 在网友的国度中共有 n 种不同面额的货币&#xff0c;第 i 种货币的面额为 a[i]&#xff0c;你可以 假设每一种货币都有无穷多张。为了方便&#xff0c;我们把货币种数为 n、 面额数组为 a[1..n] 的货币系统记作 (n,a)。 在一个完善的货币系统中&#xff0c;每一个非…

TCP/IP网络编程——关于 I/O 流分离的其他内容

完整版文章请参考&#xff1a; TCP/IP网络编程完整版文章 文章目录第 16 章 关于 I/O 流分离的其他内容16.1 分离 I/O 流16.1.1 2次 I/O 流分离16.1.2 分离「流」的好处16.1.3 「流」分离带来的 EOF 问题16.2 文件描述符的的复制和半关闭16.2.1 终止「流」时无法半关闭原因16.2…

Python 之 Pandas 时间函数 time 、datetime 模块和时间处理基础

文章目录一、time 模块1、时间格式转换图2. struct_time 元组元素结构3. format time 结构化表示二、datetime 模块1. date类2. 方法和属性3. datetime 类三、timedelta 类的时间加减四、时间处理基础Python 中提供了对时间日期的多种多样的处理方式&#xff0c;主要是在 time …

Spring架构篇--2.4 远程通信基础--Socket通信

前言&#xff1a;通信中我们常常建立socket 通过其tcp完成通信&#xff1b; 1 Socket 介绍&#xff1a; 所谓socket 通常也称作”套接字“&#xff0c;用于描述IP地址和端口&#xff0c;是一个通信链的句柄。应用程序通常通过”套接字”向网络发出请求或者应答网络请求&#…

四色菊皇家大学-BCG U2T产品宣传活动

BCG U2T产品的宣传和介绍。 2023年2月16日&#xff0c;四色菊皇家大学艺术文化中心&#xff0c;高等教育、科学、研究和创新部长Dnuch Tantodtit博士主持了BCG U2T产品的市场推广项目。四色菊皇家大学校长Saksit Anganaphanayakorn博士&#xff0c;以及U2T项目的管理人员、教职…

Ubuntu16.04使用apache创建个人用户主页并添加口令认证

文章目录一.安装apache二、apache文件和目录简述2.1 网站数据目录2.2 Apache配置文件三、创建个人用户主页3.1 开启个人用户主页功能3.2 建立目录和首页面3.3 开启模块3.4 测试四、添加口令认证4.1 生成密码数据库4.2 修改配置文件一.安装apache 创建虚拟机&#xff0c;保持默…

代码随想录【Day21】| 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先

530. 二叉搜索树的最小绝对差 题目链接 题目描述&#xff1a; 给你一棵所有节点为非负值的二叉搜索树&#xff0c;请你计算树中任意两节点的差的绝对值的最小值。 示例&#xff1a; 提示&#xff1a;树中至少有 2 个节点。 难点&#xff1a; 解答错误&#xff01;仅考虑了…

2023年软件测试工程师怎样跳槽,才能越跳越值钱?

2023年就业难&#xff1f;可那个转行干软件测试的小哥哥才刚拿到2W薪资的offer&#xff0c;紧接着又跳槽去了大厂 作为软件测试工程师怎样跳槽才能越跳越值钱呢&#xff1f; 把控好跳槽频次 我们在编写简历的时候&#xff0c;总想尽可能展示出自己的技能&#xff0c;但是简历上…

【基础算法】数的范围

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

尚医通 (二十一)预约下单

目录一、预约下单功能(一)1、需求2、搭建订单模块3、封装Feign调用获取就诊人接口4、封装Feign调用获取排班下单信息接口二、预约下单功能(二)1、实现生成订单接口三、预约下单功能(三)四、预约下单功能(四)1、生成订单后处理逻辑-封装短信接口2、生成订单后处理逻辑-更新排班数…

Allegro误删器件位号如何快速刷新回来操作指导

Allegro误删器件位号如何快速刷新回来操作指导 在用Allegro做PCB设计的时候,有时会因为误操作,把需要的丝印位号删除了,如果想把位号复原回来,可以把当前器件删除,再重新放置即可。 下面介绍在不删除器件的情况下也能快速刷新回来的方法 如下图 误操作前 误操作后,位号…

Vue3+Ts+Vite开发插件并发布到npm

依赖版本信息如下&#xff1a; "vue": "^3.2.45""typescript": "~4.7.4""vite": "^4.0.0""less": "^4.1.3""terser": "^5.16.4"npm: 8.1.0node: 16.13.0 目标&#xf…

Spring AOP(AOP概念、组成、Spring AOP实现及实现原理)

文章目录1. Spring AOP 是什么2. 为什么要用 AOP3. 怎么学 Spring AOP4. AOP 组成5. Spring AOP 实现5.1 添加 Spring AOP 框架支持5.2 定义切面和切点5.3 实现通知方法5.4 使⽤ AOP 统计 UserController 每个⽅法的执⾏时间 StopWatch5.4 切点表达式说明 AspectJ6. Spring AOP…

【SPSS】基础图形的绘制(条形图、折线图、饼图、箱图)详细操作过程

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Qt-FFmpeg开发-实现录屏功能(10)

#音视频/FFmpeg #Qt Qt-FFmpeg开发-实现录屏功能&#x1f4ac; 文章目录Qt-FFmpeg开发-实现录屏功能&#x1f4ac;1、概述&#x1f4a5;2、实现效果&#x1f4a8;3、FFmpeg录屏代码流程&#x1f441;️‍&#x1f5e8;️4、主要代码&#x1f919;5、完整源代码&#x1f90f;更…