Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >红包随机算法&微信群红包随机算法

红包随机算法&微信群红包随机算法

作者头像
恋喵大鲤鱼
发布于 2021-03-02 07:02:58
发布于 2021-03-02 07:02:58
7.5K00
代码可运行
举报
文章被收录于专栏:C/C++基础C/C++基础
运行总次数:0
代码可运行

1.前言

因疫情影响,部门 2021 年会以线上直播的形式进行,通过微信小程序展开。为活跃年会氛围,年会直播间会有抢红包环节。因产品要求,红包金额要随机生成,所以这里涉及到指定红包总额、数量和最大最小值情况下如何生成红包金额。

可以看出,红包随机金额生成的输入是一个四元组 <sum, num, min, max>,其中 sum 是红包总额,num 是红包数量,min 和 max 分别是红包最小最大值。所以这里可以抽象成如下算法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
input: <sum, num, min, max>
output: 随机红包金额数组

因为法币都有最小单位,比如人民币是分,所以上面输入四元组均可视为整数。

2.参考微信群红包算法

本质上,这和微信群红包没什么区别,发出一个固定总金额的红包,指定红包数量,那么随机分配红包金额时需要满足哪些规则? (1)所有人抢到金额之和等于红包总金额,不能超过,也不能少于; (2)抢到的红包金额至少是一分钱; (3)要保证抢到红包的人获取到的红包金额是随机的。

实际上,微信群红包的 min 是 1 分钱,max 是剩余红包金额均值的两倍,为什么是这两个值,因为这么做会保证随机值的期望值等于均值,来保证不会因为抢红包的先后顺序而造成不公平。这两个值是算法内设的,不提供给用户指定。另外总金额 sum 和数量 num 是由用户指定的。

为什么微信群红包要搞一个最大上限,因为如果不设置一个最大上限,会出现一种不公平的现象。就是越在前边领取红包的同学,其可随机范围越大,获得大额红包的几率也越高。一旦前边的同学随机到一个较大的金额,后边的同学可以随机的范围就逐步收窄,抢红包就变成了一个拼手速的游戏了。

实际上,微信群红包采用的是二倍均值法,也就是每次随机上限为剩余红包金额均值的两倍。微信群红包金额分配算法是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
每次抢红包直接随机,随机的范围是[1, 剩余红包金额均值的两倍],单位分

这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。

实际上微信群红包的算法虽然公平,但是有个缺陷,不过这个微信产品同学可以接受,只是对于用户来说体验并不是那么友好,因为有时发个群红包会出现下面这种最后一个红包金额非常大的情况。

出现这种情况的原因是,上面的随机上限 max 为剩余红包金额均值的两倍,对于最后一个红包是无法生效的。当然,出现这种情况的概率非常小。

这说明了一个什么问题呢?红包金额随机分配算法不是一个标准算法,而是产品逻辑。 如果你是产品同学,你完全可以搞一个你想要的随机分配算法,比如随机范围严格在 [min, max] 之间,或者像微信群红包那样,每次抢红包时,max 是动态变化的。

这里说下大家最关心的问题,就是如何才能抢到大红包。通过上面的介绍,结论就是除了最后一个红包金额是有可能大于均值的两倍,其他都是在 [0.01 - 剩余均值*2] 之间随机。如果红包数量充足,那么最后一位抢才有可能获得大红包。但绝大部分情况是僧多粥少,需要拼手速才能抢到红包,这种情况下,你不能保证你是最后那位抢到红包的人。

3.一个可用的随机算法

此次年会产品同学开始跟我说需要像微信群红包那样的随机分配红包金额,但是仔细研究了微信群红包的算法,才发现产品同学想要的效果和微信群红包并不同,她想要的是红包金额严格随机范围在 [min, max]。

在实现时要满足如下几个条件: (1)所有人抢到金额之和等于红包总金额,不能超过,也不能少于; (2)抢到的红包金额在 [min, max] 之间; (3)要保证抢到红包的人获取到的红包金额是随机的。

下面给一个可行的随机分配算法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// min 最小金额分 max 最大金额分 num 红包数量 sum 红包总额分
input:<min, max, num, sum>

// 参数合法性校验
step 1: min*num <= sum <= max*num 
step 2: 将 num 个在 min 填入数组
step 3: 循环随机一个范围为 [0, max - min] 数加到最小值数组中。如果随机数大于剩余金额,则取剩余金额作为随机数;如果累加值大于最大值,则取最大值与原值差值作为随机数。如果剩余金额为 0 结束循环
step 4: 如果均值靠近 min 或 max,第三步分别会出现很多 min 或者 max,看起来不够随机。这里需要经过一轮或多轮遍历,将 (min, max) 之间的数减掉部分给到 min 或者从 max 获得部分
step 5: 打乱数组顺序

注意,在第四步消除最小值或最大值,是控制在一定比例还是完全消除,也是一个产品逻辑,需要由产品同学来定。下面的实现示例,只进行一轮循环,可能会存在少量最小值或最大值。

下面以 JS 为例,给出实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// brief: 获取随机整数 [min, max]
function random(min, max) {
  const range = max - min;
  const rand = Math.round(Math.random() * range);
  return min + rand;
}

// brief: 消除最小值和最大值
function smooth(min, max, arr) {
  for (let i = 0; i < arr.length; i++) {
    if (!(min < arr[i] && arr[i] < max)) {
      continue
    }
    for (let j = 0; j < arr.length; j++) {
        // 消除最小值
        if (arr[j] === min) {
          let rm = Math.floor((arr[i] - min)/10)
          arr[i] -= rm
          arr[j] += rm
          break
        }
        // 消除最大值
        if (arr[j] === max) {
          let rm = Math.floor((max - arr[i])/10)
          arr[i] += rm
          arr[j] -= rm
          break
        }
    }
  }
}

// brief: 打乱数组顺序
function shuffle(arr) {
  arr.sort(() => Math.random() - 0.5);
}

// brief: 生成随机整数数组
function randnum(min, max, num, sum) {
  // step 1 检查参数
  if (min <= 0 || max <= 0 || num <= 0 || sum <= 0) {
    return [];
  }
  if (!(min * num <= sum && sum <= max * num)) {
    return [];
  }

  // step 2 将 num 个在 min 填入数组
  var arr = new Array(num).fill(min);
  
  // step 3 循环随机生成[0, max-min]加到最小值数组
  let leftTotal = parseInt(sum - min*num);
  LABEL:
  while(true) {
    for (let i = 0; i < num; i++) {
        let rand = random(0, parseInt(max-min));
        // 如果随机数大于剩余金额,则取剩余金额作为随机数
        if (rand > leftTotal) {
            rand = leftTotal;
        }
        // 如果累加值大于最大值,则取最大值与原值差值作为随机数
        if (arr[i] + rand > max) {
            rand = max - arr[i];
        }
        arr[i] += rand;
        leftTotal -= rand;
        if (leftTotal === 0) {
            break LABEL;
        }
    }
  }
  
  // step 4 消除大部分最小值和最大值
  smooth(min, max, arr)
  
  // step 5 打乱数组顺序
  shuffle(arr)
  
  return arr;
}

上面的代码可以在 Online NodeJS IDE 执行。

下面采用两组入参,均值分别靠近最小值和最大值来观察多次运行后的输出结果。

第一组入参,最小金额 5 元,最大金额 50 元,数量 10 个,总金额 100 元。均值 10 靠近最小值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 单位为分
console.log(randnum(500, 5000, 10, 10000))

// 第一轮结果
[ 516, 3317, 646, 515,  677, 636, 1861,  501, 518, 813 ]

// 第二轮结果
[ 724,  502,  500, 726, 2761, 2740, 500,  502,  522, 523 ]

// 第三轮结果
[ 500, 500, 1009, 899, 504, 4492, 500, 505,  551, 540 ]

第二组入参,均值靠近最大值,最小金额 5 元,最大金额 50 元,数量 10 个,总金额 400 元。均值 40 靠近最大值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 单位为分
console.log(randnum(500, 5000, 10, 40000))

// 第一轮结果
[ 4757, 4787, 3001, 1486, 4610, 4667, 4149, 4974, 2573, 4996 ]

// 第二轮结果
[ 4684, 2155, 3838, 4853, 4915, 3182, 4787, 3074, 4984, 3528 ]

// 第三轮结果
[ 4017, 4986, 3122, 4843, 4807, 4867, 2361, 3586, 3734, 3677 ]

从上面的实验结果可以看出,相同入参多次运行结果是不同的。如果均值靠近最小值或者最大值,结果可能分别会出现多个最小值和最大值,这个可以通过多次执行 smooth 函数来完全消除。


参考文献

[1] 漫画:如何实现抢红包算法? [2] 微信拼手气红包背后的算法逻辑

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
最全解密微信红包随机算法(含代码实现)
这个系列文章已经整理了10篇,但都没有涉及到具体的红包算法实现,主要有以下两方面原因。
JackJiang
2020/09/18
15.1K0
最全解密微信红包随机算法(含代码实现)
社交软件红包技术解密(十一):最全解密微信红包随机算法(含代码实现)
这个系列文章已经整理了10篇,但都没有涉及到具体的红包算法实现,主要有以下两方面原因。
JackJiang
2020/08/26
1.4K0
面试场景题:如何设计一个抢红包随机算法
这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。
卷福同学
2025/03/19
902
红包算法揭秘!哪段代码让你只抢了0.01元?
普通随机:用余下的值为最大区间进行随机,但可能不均匀,有些人一把随到99,下面很多人都没得随机了。
腾讯云开发者
2024/05/23
4240
红包算法揭秘!哪段代码让你只抢了0.01元?
Java实现微信抢红包
场景:100块钱红包,群内50人,红包数量为20个,30个人将抢不到红包
疯狂的KK
2021/03/03
7.3K0
Java实现微信抢红包
别人在抢红包,而程序员在研究微信红包的算法实现
链接:http://blog.cqcoder.com/微信红包的算法实现探讨/ 突发奇想给校友微信群发了红包,我设定红包总额为10元,支持28个人随机领取 于是一个有趣的结果出现了 A 领取了 0.
IT大咖说
2018/04/04
1.7K1
别人在抢红包,而程序员在研究微信红包的算法实现
c语言抢红包小游戏
有问题留言 #include #include #include int main() { while (1) { printf_s("请输入数字:\n"); printf_s("1:抢红包\n"); printf_s("2:退出程序\n"); int a; scanf_s("%d", &a); if (a == 1) { printf_s("请输入数字:\n"); printf_s("1:随机红包\n"); printf_s("2:平均红包\n")
川川菜鸟
2021/10/18
2.2K0
算法思考:红包金额生成
最近在整理过去的项目时,回顾了某年红包活动,其中涉及红包金额计算的算法。近些年各家大厂举办的春节红包活动越来越完善,关于活动背后的整体设计介绍、分析、探讨层出不穷。本篇先不关注整体架构,选择红包金额的计算方法作为分析内容。
程序员架构进阶
2021/04/29
1.4K0
听说你会架构设计?来,弄一个红包系统
大家好,我是小❤,一个漂泊江湖多年的 985 非科班程序员,曾混迹于国企、互联网大厂和创业公司的后台开发攻城狮。
xin猿意码
2023/12/14
1.4K0
听说你会架构设计?来,弄一个红包系统
Java实现微信抢红包
这个算法可以把总金额想象成一条线段,每个人都有机会切一刀,前面的人切完(有失公平性,会出现第一个切一大段的情况,后面需要改造),剩下的后面的人再接着切剩下的,这样越是前面的人截取的长度(理解成领取到的红包金额)越大的概率就越大。
疯狂的KK
2023/06/30
5250
听说你会架构设计?来,弄一个微信群聊系统
当我那天拿着手机,正在和朋友们的微信群里畅聊着八卦新闻和即将到来的周末计划时,忽然一条带着喜意的消息扑面而来,消息正中间写着八个大字:恭喜发财,大吉大利。
xin猿意码
2023/11/02
8760
听说你会架构设计?来,弄一个微信群聊系统
微信抢红包模拟实现
设剩余红包金额为 M,剩余人数为 N,每次抢到的金额 = 随机区间(0,M / N * 2)
yuanshuai
2023/11/17
5460
红包随机算法,给定一定的金额,一定的人数,保证每个人都能随机获得一定的金额。
随机法,每次抢红包时计算出本次能够获得的最小金额和最大金额,然后在这个区域间中取一个随机值并计算得出这次抢到的红包金额,这种方法,优点是实现简单,但是,先抢的人会很赚,抢到大红包的概率很高,越到后面的人越吃亏。 
翎野君
2023/05/12
4350
社交软件红包技术解密(十三):微信团队首次揭秘微信红包算法,为何你抢到的是0.01元
在上一篇《来看看微信十年前的IM消息收发架构,你做到了吗》的文章中,有用户提到想了解自己每次微信红包只能抽中 0.01 元的反向手气最佳是怎么在技术上实现的,于是就有了本篇文章的诞生。
JackJiang
2024/06/06
8200
社交软件红包技术解密(十三):微信团队首次揭秘微信红包算法,为何你抢到的是0.01元
PHP实现的随机红包算法示例
算法有很多种, 可以自行选择, 主要的"架构" 是这样的, 用redis decr()命令去限流, 用mysql去记录各种需要的数据
用户8449980
2021/07/13
9120
微信 随机红包的实现原来如此简单
微信的抢红包的时候,你知道红包的分发规则是怎么样的吗?你是否总在祈祷着能够抢到一个最大的成为手气最佳的哪一个人,那么红包的方法规则是怎么样的呢?是如何实现的呢?下面就来了解一下吧!!
全栈程序员站长
2022/07/08
1.2K0
Java实现抢红包算法,附完整代码(公平版和手速版)
这是一种很合理很公平的抢红包算法了,绝对不会让你拼手速的,就别天真了。 在此我们假设 红包剩余金额为 M 红包剩余数量为 N 这种算法就是每次都在区间[0,M/N×2] 随机取一个数 假设100元红包发10个人,那么合理的做法应该是每个人领到10元的概率相同。 第一个人随机金额的范围为[0,100/10×2] ,也就是[0,20],这样平均可以领到10元,此时剩余金额为100-10=90。 第二个人随机金额的范围为[0,90/9×2] ,也就是[0,20],这样平均也可以领到10元,此时剩余金额为90-10=80。 第三个人随机金额的范围为[0,80/8×2] ,也就是[0,20],这样平均也可以领到10元。 这样推导下去,每个人领到相同金额的概率应该就是相同的了。
Java团长
2020/09/22
1.9K0
Java实现抢红包算法,附完整代码(公平版和手速版)
抢红包统计学(技术贴,知道为啥自己越抢越穷了吧)
抢红包统计学(技术贴,知道为啥自己越抢越穷了吧) 一、引言 过年前微信群里面流行起来一种“红包接力”的玩法,大概的规则是:群里面先由一人发一个红包,然后大家开始抢,其中“手气最佳”的那个人继续发新一轮的红包,之后不断往复循环。 这时候大家或许就会问了,一直这么玩下去会有什么结果呢?是“闷声赚大钱”了,还是“错过几个亿”了?是最终实现“共同富裕”了,还是变成“寡头垄断”了?要解答这些问题,就得先了解一些统计学的知识,然后模拟一些随机实验,得到的结果或许会让你大跌眼镜呢。 二、红包初级模型:“切面条法” 微信
架构师之路
2018/02/28
1.4K0
抢红包统计学(技术贴,知道为啥自己越抢越穷了吧)
实现一个简单的抢红包的单机控制台游戏
第二个问题:数据存储,这里简单的使用了map做数据的存储,这是一种内存性存储,程序运行完就释放了,那么怎么将数据持久化储存呢?我们常用的方案是DB或本地文件。在后续的文章中,将对数据持久化作为一个专题进行展开,并实现一个简单的本地DB进行数据的存储。
暮雨
2019/08/10
3960
除夕抢红包你准备好了吗?大数据教你怎么抢红包手气最好
<数据猿导读> 还记得元旦抢红包的空前盛况吗?微信表示也hold不住啦,然而元旦过去了,春节还会远吗,今天小编特意准备了一份抢红包秘籍,让你在除夕晚成为手气第一人 抢红包眼疾手快, 发红包精打细算,
数据猿
2018/04/19
1.2K0
除夕抢红包你准备好了吗?大数据教你怎么抢红包手气最好
推荐阅读
相关推荐
最全解密微信红包随机算法(含代码实现)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验