如果你有一台智能手机,如果你装了一个名叫微信的软件,那么你今年的春节很可能是在下面这样的场景中度过的(图片来自微信群):
这也使得众多的网络大V发出了下面的感慨:
而最近几天不少微信群里面又流行起来一种“红包接力”的玩法,大概的规则是:群里面先由一人发一个红包,然后大家开始抢,其中金额最大的那个人继续发新一轮的红包,之后不断往复循环。
这时候大家或许就会问了,一直这么玩下去会有什么结果呢?是“闷声赚大钱”了,还是“错过几个亿”了?是最终实现“共同富裕”了,还是变成“寡头垄断”了?要回答这些问题,我们不妨用统计模拟的方法来做一些随机实验,得到的结果或许会让你大跌眼镜呢。
模拟红包发放
要进行模拟实验,就需要设定一个红包金额的分配机制。但由于微信红包的算法并没有公开,所以在这里我们使用了一个简化的模型,即假设抢到红包的人分得的金额占总金额的比例(每个人对应一个比例,所以这是一个向量,且其总和为1)服从一个 Dirichlet 分布,参数为 \(\vec{\alpha}=(\alpha, \alpha, \ldots, \alpha)\)
,即 \(\vec{\alpha}\)
是一个各分量都相等的向量,由一个参数 \(\alpha\)
决定。对于不熟悉 Dirichlet 分布的读者,可以参考rickjin 大侠的文章以及 Dirichlet 分布的维基页面。
在这个简化的模型里,\(\alpha\)
决定了红包发放的“公平”程度。\(\alpha\)
越大,每人分得的金额比例就越倾向于平均,反之则波动性越大。下面展示了两组 Dirichlet 分布的随机数,我们假定红包金额分成5份,分别考察 \(\alpha=1\)
和 \(\alpha=10\)
时的金额比例,其中每一行是一次模拟。本文的最后附上了在R中生成 Dirichlet 分布随机数的程序。
rdir(3, rep(1, 5)) ## alpha = 1
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.02669467 0.248426309 0.23745777 0.35864430 0.12877695
## [2,] 0.32398351 0.472000506 0.11704852 0.08291485 0.00405262
## [3,] 0.29309424 0.080879260 0.09338722 0.03765236 0.49498692
rdir(3, rep(10, 5)) ## alpha = 10
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.2459250 0.2722147 0.1717301 0.1398133 0.1703169
## [2,] 0.1956686 0.2058377 0.1540937 0.3322662 0.1121338
## [3,] 0.1729287 0.1807517 0.1597919 0.2075967 0.2789310
可以看出,当 \(\alpha=1\)
时,金额分配的变动性非常大,比如对于第一组随机数,如果红包总金额是100,那么领得最多的人可以得到35.86元,而最少的只有2.67元。而在 \(\alpha=10\)
的情形下,金额的分配就平均多了。
模拟接力游戏
下面我们就来模拟红包接力的游戏。首先假设我们有一个50人的群,每人初始手头上的可用金额为50元(这里是为了产生“破产”现象而故意放低的,土豪们请忽略此设定),根据规则,每次红包的总金额是20元,发放给10个人,其中抢得最大红包金额的人将发出下一轮的红包。如果某人发完红包后余额变成了负值,就不能再继续抢红包(请原谅这个丧心病狂的设定……)。
本文最后给出了模拟红包接力的R语言代码,其中我们依然做了很多简化,比如假设抢到红包的人是在参与游戏的人中间均匀分布的(排除了资产为负的人)。在实际情况中,大家可能会根据自己余额的多少来决定是否继续参加,但在此我们忽略了这种可能。
我们让红包接力100次,最后大家的余额如下:
set.seed(123)
hb_experiment(param)$last_balance
## [1] 31.2447 82.6933 18.0721 44.5619 62.8697
## [6] 33.3992 47.0031 45.5506 77.1086 70.4357
## [11] 54.2828 26.9817 54.7438 80.3029 28.3159
## [16] 43.9803 48.8019 82.6942 82.9376 -10.9989
## [21] 34.3014 80.6406 60.6780 47.3356 40.1266
## [26] 52.5458 23.3861 62.6662 92.2020 72.4268
## [31] 41.5504 40.1163 50.5079 81.2957 51.1659
## [36] 43.3606 34.9284 64.3786 42.7006 -8.9036
## [41] 9.1015 78.6058 46.3494 64.1757 61.9002
## [46] 13.6061 50.0068 68.5091 41.2131 54.1413
可以看出,有两位朋友不幸破产了,而最后资产最多的有92.20元,几乎翻了一倍。一个很明显的事实是,破产的玩家都是因为“中头奖”中得太多了,导致入不敷出。相反,最终收得92.20元的这位玩家属于“闷声发大财”。经统计,“它”获得第二名3次,第三名2次,第四名2次,第五名4次,等等。
当然,概率面前人人平等,没有谁能预知自己抽中红包后会是最大的还是最小的,所以从对称性的角度考虑,个人选择的结果是完全随机的。但是,从整个群的角度来看,有一个指标却在悄悄发生变化,那就是这个群的“贫富差距”。
平均还是独大?
我们注意到,在游戏最开始的时候,大家的资金都是一样的(50元),而在100次接力之后,几家欢喜几家愁,贫富差距被拉大了。于是我们有两个很自然的问题:1. 如何量化这种贫富差距?2. 随着游戏的进程,贫富差距会有怎样的变化?
对于第一个问题,我们可以借用经济学中的一个概念来予以回答,那就是所谓的“基尼系数”(Gini Coefficient)。基尼系数通常被用来衡量一个国家居民收入的公平性,其取值在0到1之间,越大表示贫富差距越大,即少部分的人掌握了这个经济体大部分的收入。基尼系数的计算公式可以在它的维基页面中找到,本文最后也给出了相应的R代码。对于之前的模拟游戏结果,计算出的基尼系数是0.2551。
这个结果的绝对数值可能并没有太大的意义,因此我们在每一轮接力之后都计算出当时这个群的基尼系数,然后观察它的变化。结果如下:
在这里我们将接力次数延长到了500次。可以看出,随着接力的进行,基尼系数的整体趋势是在不断变大的,意味着贫富差距会随着游戏的进行变得越来越大。这其实很好理解:总是会有人因为拿了太多头奖而破产,这样财富会在越来越少的人中间进行分配,所以相应地贫富差距就拉大了。
为了更直观地展示50个玩家的余额变化,我们可以看一下下面的这个动画,其中每幅图代表一个玩家的资金余额。
红包越“公平”,贫富差越大
前面提到,在我们的模型中有一个参数 \(\alpha\)
用来控制红包金额分配的“公平”程度(或者更准确地说,是“平均”的程度,因为就机会而言,每个人分得金额的可能性都是相同的,但就每一次实际分得的金额而言,\(\alpha\)
越大,这种分配越倾向于平均,即结果的波动性越小)。下图展示了一组随机模拟实验的结果,其中我们模拟了20次红包接力的游戏,10次取 \(\alpha=2\)
, 另外10次取 \(\alpha=20\)
。每次游戏中,红包都接力了500次。
可以看出,红线和蓝线虽然有所重叠,但总体来看蓝线的取值要比红线更大,也就是说,红包金额越“公平”,贫富差距反而会越大。
这个结论看起来可能有些反直觉,但其实也合情合理:如果红包的分配是绝对公平的,那么第一名得到的金额就将是2元,而下一轮又必须送出20元,所以总共亏损18元;如果红包金额的波动性很大,就会有一部分人得到的金额小于2元,而第一名就会得到更多,也就更不容易破产。所以说,一个规则是否真的“公平”,不能只看其表面。
红包金额越滚越大
我们最后再看一个这样的规则:第一个红包金额为20元,下一个为21元,再下一个为22元……到了30元后,又逐渐递减至20元,以此反复。我们对代码稍作修改后,同样画出基尼系数的变化图,对比这两种规则产生的结果:
很明显,当红包的金额发生这种变动后,贫富差距又进一步拉大了。
更多
除了本文考察的这些可能影响金额分配的因素之外,感兴趣的读者还可以利用文中的代码(可能需要稍作修改)继续考察其他因素对贫富差距的影响,比如红包人数,初始金额等等。最后提醒大家的是,红包主要还是在过年的时候图个喜庆,游戏有风险,抢包需谨慎。:D
附:文本用到的R预言代码
rdir = function(n, shape)
{
len = length(shape)
mat = matrix(rgamma(len * n, shape), len)
sums = colSums(mat)
t(mat) / sums
}
gini = function(x)
{
x = sort(x)
n = length(x)
2 * sum(seq_along(x) * x) / n / sum(x) - (n + 1) / n
}
param = list(group_size = 50, ## 群大小
init_balance = 50, ## 每人初始金额
hb_amount = 20, ## 每次红包的总金额
hb_size = 10, ## 红包发给多少人
niter = 100, ## 红包接力次数
alpha = 2 ## 红包金额分配参数
)
hb_experiment = function(p)
{
id = 1:p$group_size ## 群成员 ID 编号
balance = rep(p$init_balance, p$group_size) ## 当前每人资产
bal = matrix(0, p$niter, p$group_size)
for(i in 1:p$niter)
{
## 破产的就别再玩啦
players = id[balance > 0]
if(i == 1)
{
## 第一轮随机挑一个人发红包
host = sample(players, 1)
} else {
## 后面就找抢得最多的
host = winners[which.max(winner_amount)]
}
## 红包主掏钱
balance[host] = balance[host] - p$hb_amount
## 手快有,手慢无
winners = sample(players, p$hb_size)
## 每人领取的红包金额
winner_amount = p$hb_amount * c(rdir(1, rep(p$alpha, p$hb_size)))
balance[winners] = balance[winners] + winner_amount
bal[i, ] = balance
}
return(list(balance = bal, last_balance = balance))
}
set.seed(123)
b = hb_experiment(param)$last_balance
gini(b)
发表/查看评论