关于红包算法的问题

2016-07-24 23:28:39 +08:00
 Exin

微信抢红包大家都很熟悉。

现在考虑这样一种情况:将 100 元的红包分为 10 份,每份最低 5 元最高 15 元,金额精确到 0.01 元。那么如果算法没问题的话,每个人的期望值自然是平均值,即 100 元 /10 人=10 元 /人。

最先想到的算法:

在 5~15 内生成随机数,即为当前用户的红包值。这种方式会出现分配后余额不足以按要求继续分配(剩余过多或过少)的情况,导致之后的红包内容均为最大值或最小值。这似乎也是微信红包的算法。

后来考虑了一种避免后期红包数值统一于极值的方式:

根据红包总份数 N 生成 N 个 0~1 内的随机数,按照各随机数在这些随机数之和中所占的比重,分配可分配的那部分金额(总金额 - 最小值 * 份数)。当出现不符合分配要求的情况时,对已求得的每个随机数进行求根运算(也可以是其他运算,只要减小方差就行)并重新求和。(代价是必须一次性得出所有红包的分配方案)

然后问题来了:

我统计了上述两种方法都结果,均没有出现众数集中在平均值的结果:

算法 1:

算法 2:

按我的理解,众数应当接近于期望值才比较理想。求大神指导下问题所在,以及有没有更好的算法

3538 次点击
所在节点    问与答
44 条回复
billlee
2016-07-25 01:18:59 +08:00
什么叫做众数集中在平均值?你是想说正太分布吗?
aprikyblue
2016-07-25 01:49:46 +08:00
是想说萝莉分布吗:doge:
HypoChen
2016-07-25 02:01:23 +08:00
我有一个方案,和你的方案二差不多,不过我不知道众数会在哪 23333 ,想问你怎么画的图 23333

```
def comp(sum=100, n=10):
result = []
while n > 1:
min = sum - 15 * (n - 1) if sum - 15 * (n - 1) >= 5 else 5
max = sum - 5 * (n-1) if sum - 5 * (n-1) <= 15 else 15
rand = random.uniform(min, max)
rand = round(rand, 2)
result.append(rand)
sum -= rand
n -= 1
result.append(round(sum, 2))
return result
```
Valyrian
2016-07-25 02:09:14 +08:00
Google 搜索:排列组合 插板法
3dwelcome
2016-07-25 02:23:37 +08:00
还是第一种算法、只是把最后那个人多出来的钱平摊到每个人头上既可。

比如生成 10 次六元到十二元的随机数、加起来如果是 110 不是 100 、那么每个人的红包都按照比例少拿 1 块。也就不存在最后一个人抽到太多或太少的情况、因为都被前面九个人均摊了。
debiann
2016-07-25 08:17:10 +08:00
每人先拿 5 元,剩下 5000 个一分随机分发,拿满 15 封顶
Exin
2016-07-25 10:12:42 +08:00
@billlee 不仅仅是正太分布,算法 2 的结果也算是一种正态分布吧?
@aprikyblue

@HypoChen 用的是 Google charts

@debiann 这样算是不是会很费时?要给每个 1 分做一次循环?
Exin
2016-07-25 10:18:23 +08:00
@HypoChen 你的算法应该是和我的方案一 一样的吧,只是每次界定分配金额的时间点不同
Exin
2016-07-25 10:42:32 +08:00
@3dwelcome 也考虑过这种方法,以为效果不佳就没去写。刚刚写了一下,结果比较惊喜,我贴在 Append 里了。
Exin
2016-07-25 10:50:28 +08:00
@3dwelcome 贴之前仔细看了看,是我写错了……还是贴一下结果:
Exin
2016-07-25 11:06:38 +08:00
@debiann
我写了一下,结果如图


如果是 5 ~ 15 元,平均每人 10 元的条件,这个算法是比较理想的。
但是在图中 6 ~ 12 元,平均每人 10 元的条件下暴露了缺陷:
以期望值为中心正态分布,仅适用于最大值、最小值的平均值等于期望值的情况。
imdoge
2016-07-25 11:09:43 +08:00
我的想法:每人先拿 5 元
剩下 50 元,随机生成 0~10 的随机数,剩余金额再随机 0~x , x 是 min(10, 剩余金额)
imdoge
2016-07-25 11:10:54 +08:00
重复 10 次……
Exin
2016-07-25 11:14:58 +08:00
@imdoge 这和我的算法 1 、 3L 的算法,有什么区别吗?
3dwelcome
2016-07-25 11:21:33 +08:00
我来贴一下结果,用 100 元分十份,每份 7 元~ 13 元随机分布。循环很多次,然后绘图统计每个人拿到钱的概率。

Asimov
2016-07-25 11:30:43 +08:00
⎝≧⏝⏝≦⎠ 这么简单的问题被你描述的那么复杂。
Exin
2016-07-25 11:43:42 +08:00
@Valyrian 对于单份金额上下限的限制,插板法中该如何处理呢?
Exin
2016-07-25 11:44:19 +08:00
@Asimov 那麻烦你说一下优秀的解法?
Exin
2016-07-25 11:45:23 +08:00
@3dwelcome 能否贴一下 100 元, 10 人, 6 ~ 12 元条件下的结果?
Exin
2016-07-25 11:46:54 +08:00
@3dwelcome 因为(13 + 7) / 2 = 10 ,(15 + 2) / 2 = 10 ,都是很简单的情况。我真正考虑的是两侧不均衡的条件下的算法。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/294633

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX