微信红包算法概率比较

前言

面试,或者社会,网络,甚至电视上都会谈到的一个问题,
关于微信红包是否每个人都概率一样的问题,
于是在数学上做了一个证明,并写了个程序验算了公式

提出想法

脑子里第一个想法就是:m1 = rand(0.01, m - 0.01*n),其中:

rand: 随机函数
m1: 抢到的金额 
m: 当前钱包剩余金额
n: 当前还未抢红包的人数

意思就是,每次抢到的钱包金额是保证剩余的人至少能抢包1分钱之后,剩余的金额随机抢一个金额,
相信大多数人的第一反应就是这一个意思,这样是对的吗?

数学来了

从数学角度可以证明,这样是不平均的,证明如下:

定义M为总金额,
第一次抢红包的金额为, m1 = [0.01, M), 平均金额金额为 m1 = M/2
第二次抢红包的金额为,m = M - m1, m2 = [0.01, m), 平均金额为 m2 = (M - m1) / 2 = M/4
同理,
第三次抢红包的金额为,m = M - m1 - m2, m3 = [0.01, m),平均金额为 m3 = (M - m1 - m2) / 2 = M/8
...
第n次平均金额为 mn = (M - m1 - m2 - ... - mn-1) / 2

由上易得:
m1 = M/2 > m2 = M/4,
m2 = M/4 > m3 = M/8 
...


也可以得 m = M/(2^n), 
当n增大时,m减小。可知m是关于n一个单调递减函数。

得出结论,先抢到的期望大于后面的期望值,抢到的红包金额不平均,由此特地写一个程序证明:
100块钱,10个红包,循环了10000次,平均值如下:
在这里插入图片描述

上面图标金额单位是分,总额为9995分,丢的5分是因为每次的金额除以10000时,整数丢精度了,没处理,但是所有抢到的总金额是正确的。这个不影响结论,所以忽略误差的5分钱。

可以明显看到测试后的数据满足公式推论,上一次是下一次的2倍。但是最后的几次不满足。原因是因为红包最后一次有一个pick all的操作,即是第10个人,不管剩余多少,都全部是第10个人的。

正确的算法来了

根据参考文章提到的红包算法公式:m1 = rand(0.01, (m/n)*2), 其中,

rand: 随机函数
m1: 抢到的金额 
m: 当前钱包剩余金额
n: 当前还未抢红包的人数

简单证明:

第一次抢红包的平均金额是 m1 = M/n,
第二次抢红包的平均金额是 m2 = M/n,
第三次抢红包的平均金额是 m3 = M/n,
...
第n次抢红包的平均金额是 mn = M/n,

即:
无论第几次抢红包,抢到的红包的平均值都是M/n.
这才使得红包分部均匀,先抢还是后抢都是一样的期望值。

这个没有写程序了,在该文章中有程序证明:

参考文章:

  1. https://www.zybuluo.com/yulin718/note/93148
  2. https://cloud.tencent.com/developer/article/1699931
  3. https://www.zhihu.com/question/22625187
### 微信红包分配算法的Python实现 为了模拟微信红包的随机分发机制,下面提供了一种基于概率分布的方法来确保金额合理分配给每个参与者。该方法首先设定总金额以及参与人数,在满足最小额度的前提下尽可能均匀地拆分剩余部分。 ```python import random def distribute_red_packet(total_amount_cents, num_people): """ 模拟微信红包分配逻辑 参数: total_amount_cents (int): 总金额(以分为单位) num_people (int): 参与瓜分的人数 返回: list[int]: 各自获得的金额列表(以分为单位) """ if num_people <= 0 or total_amount_cents < num_people * 1: raise ValueError("参数错误") amounts = [] remaining_total = total_amount_cents for i in range(num_people - 1): upper_bound = min(remaining_total - (num_people - i - 1), int((remaining_total / float(num_people - i)) * 2)) amount_for_person = random.randint(1, upper_bound) amounts.append(amount_for_person) remaining_total -= amount_for_person # 将最后一份补足至总额 amounts.append(remaining_total) return amounts if __name__ == "__main__": TOTAL_AMOUNT_YUAN = 100 # 即1元=100分 PARTICIPANTS_COUNT = 5 result_in_cents = distribute_red_packet(TOTAL_AMOUNT_YUAN*100, PARTICIPANTS_COUNT) results_in_yuan = ["%.2f" % (amount/100.) for amount in result_in_cents] print(f"{PARTICIPANTS_COUNT}个人平分{TOTAL_AMOUNT_YUAN}.00元的结果如下:") for idx, value in enumerate(results_in_yuan, start=1): print(f'第 {idx} 位用户得到:{value} 元') ``` 上述代码实现了微信红包的核心功能——将指定数量的资金按照一定规则平均又不失趣味性地分割开来[^2]。通过调整`upper_bound`计算方式,可以控制单个红包的最大限额,从而避免极端情况的发生;同时保证每个人至少能拿到一分钱以上的小额奖励。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值