本文通過簡化了社會財富分配的過程,使用Python進行模擬計算,得出了幾個有趣的結論。
本文的靈感來源于城市數(shù)據(jù)團發(fā)布的一篇文章:該如何面對這個殘酷的世界?
在這篇文章中,把社會財富分配問題簡化成一個零和游戲,游戲基礎規(guī)則如下:
房間里有100個人,每人都有100元錢,他們在玩一個游戲。每輪游戲中,每個人都要拿出一元錢隨機給另一個人,最后這100個人的財富分布是怎樣的?
結果更符合均勻分布、正態(tài)分布還是冪律(power law)分布?
接下來我們通過參考蒙特卡羅模擬算法的思想,使用Python對這個游戲的過程進行模擬,得出結論。
如果還不了解蒙特卡羅模擬算法的,可以參考我的上一篇文章:如何通過Python實現(xiàn)蒙特卡羅模擬算法
1.財富分配模型
模型假設
- 每個人初始基金100元;
- 從18歲到65歲,每天玩一次,簡化運算按照一共玩17000輪;
- 每天拿出一元錢,并且隨機分配給另一個人;
- 當某人的財富值降到0元時,他在該輪無需拿出1元錢給別人,但仍然有機會得到別人給出的錢。
Python模擬
有了以上的模型假設,我們就可以開始使用Python進行模擬游戲。
首先需要構造初始數(shù)據(jù)集,給100個玩家,每個人分配初始資金100元:
# 構造初始數(shù)據(jù)集:100個玩家,每個人都有100元初始資金
players_num = 100
players = range(1, players_num+1) # 玩家編號
df = pd.DataFrame({
'player': players,
'money': [100] * players_num
})
接著,模擬整個游戲過程,把每一輪的財富分配結果都保存下來:
result = [] # 存儲每次分配結果
total_round = 17000 # 總共輪次
# 保存還未開始游戲時每個玩家的的財富
result.append([0] + df['money'].to_list())
for round in range(1, total_round+1):
# 幸運鵝數(shù)量
lucky_guys_num = len(df[df['money'] > 0])
# 每個人的財富都減1(除非沒錢了)
df['money'] = df['money'].apply(lambda x: x-1 if x > 0 else 0)
# 計算每個人增加的金額
lucky_guys = np.random.choice(players, size=lucky_guys_num) # 有多少個人-1,就抽取多少次
lucky_guys_bonus = Counter(lucky_guys) # 幸運鵝對應的獎勵金額
df['money'] = df.apply(lambda row: lucky_guys_bonus.get(row['player'], 0) + row['money'], axis=1)
result.append([round] + df['money'].to_list()) # 輪次以及每個玩家的財富
print(f'Round {round}')
# 所有財富分配的結果
result_df = pd.DataFrame(result, columns=["round"] + list(players))
模擬完成后,我們可以查看最后一輪的財富分布情況:
# 指定查看第幾輪的分配結果
round = 17000
show_df = result_df[result_df['round'] == round][players].iloc[0]
# 按照財富從小到大排序
show_df.sort_values(ascending=True, inplace=True)
show_df.reset_index(drop=True, inplace=True)
# 財富分配情況
plt.figure(figsize=(20, 10))
plt.grid()
plt.bar(show_df.index, show_df.values)
可以看到,即使在最公平的規(guī)則下,最終依然呈現(xiàn)出了貧富懸殊的局面:

同時,我們把整個變化過程也動態(tài)展示出來:

為了對貧富懸殊的程度進行量化,我們采用了基尼系數(shù)進行衡量。
基尼系數(shù)的Python實現(xiàn),參考:基尼系數(shù)如何計算?
# 基尼系數(shù)計算的函數(shù)
def gini_coef(wealths):
cum_wealths = np.cumsum(sorted(np.append(wealths, 0)))
sum_wealths = cum_wealths[-1]
xarray = np.array(range(0, len(cum_wealths))) / np.float(len(cum_wealths)-1)
yarray = cum_wealths / sum_wealths
B = np.trapz(yarray, x=xarray)
A = 0.5 - B
return A / (A+B)
有了這個計算方法,我們就可以計算每一輪游戲分配結束后的基尼系數(shù):
# 計算每一輪結束后的基尼系數(shù)
gini_list = [gini_coef(item[list(players)]) for i, item in result_df.iterrows()]
# 基尼系數(shù)的變化曲線
fig = plt.figure(figsize=(10, 6))
plt.plot(gini_list)
plt.annotate(
"%.2f" % gini_list[-1],
xy=(len(gini_list)+1, gini_list[-1]),
xytext=(16500, 0.4),
arrowprops=dict(facecolor='black', arrowstyle='->')
)
可以看出,基尼系數(shù)在前4000輪游戲中是變化最為劇烈的,后面才逐步平緩下來,當游戲結束時,基尼系數(shù)已經(jīng)高達0.45,而0.45已經(jīng)是屬于收入差距較大的了。

2.允許借貸會如何呢?
模型假設
在第二個模型中,我們假設允許玩家借貸,意味著當玩家資產(chǎn)為負數(shù)時,仍然可以參與游戲,這與現(xiàn)實更為接近。
玩家從18歲開始,在經(jīng)過17年后為35歲,這個期間共進行游戲6200次左右,則此刻查看財富情況,將財富值為負的標記成“破產(chǎn)”,研究該類玩家在今后的游戲中能否成功“逆襲”(財富值從負到正為逆襲)。
Python模擬
首先初始數(shù)據(jù)集和第一個模型一致,在此不再贅述,接下來模擬游戲過程:
result = [] # 存儲每次分配結果
total_round = 17000 # 總共輪次
# 保存還未開始游戲時每個玩家的的財富
result.append([0] + df['money'].to_list())
for round in range(1, total_round+1):
# 幸運鵝數(shù)量
lucky_guys_num = players_num
# 每個人的財富都減1
df['money'] = df['money'].apply(lambda x: x-1)
# 計算每個人增加的金額
lucky_guys = np.random.choice(players, size=lucky_guys_num) # 有多少個人-1,就抽取多少次
lucky_guys_bonus = Counter(lucky_guys) # 幸運鵝對應的獎勵金額
df['money'] = df.apply(lambda row: lucky_guys_bonus.get(row['player'], 0) + row['money'], axis=1)
result.append([round] + df['money'].to_list()) # 輪次以及每個玩家的財富
print(f'Round {round}')
# 所有財富分配的結果
result_df = pd.DataFrame(result, columns=["round"] + list(players))
同樣經(jīng)過模擬游戲,查看最終結果,這次我們把在第6200次游戲時財富為負數(shù)的玩家標記為破產(chǎn),用紅色展示:
# 6200次時財富為負數(shù)的玩家ID
temp_df = result_df[result_df['round'] == 6200].iloc[0]
loser_ids = list(temp_df[temp_df.values < 0].index)
print(f"6200次時財富為負數(shù)的玩家ID: {loser_ids}")
# 指定查看第幾輪的分配結果
round = 17000
# 指定輪次結果
round_df = result_df[result_df['round'] == round][players].T.reset_index()
round_df.columns = ['player', 'money']
# 6200次游戲破產(chǎn)時的顏色設置為紅色,其他人的顏色為藍色
round_df['color'] = round_df['player'].apply(lambda x: 'red' if x in loser_ids else 'blue')
# 按照財富從小到大排序
round_df.sort_values(by='money', ascending=True, inplace=True)
round_df.reset_index(drop=True, inplace=True)
# 財富分配情況
plt.figure(figsize=(20, 10))
plt.grid()
plt.bar(round_df.index, round_df.money, color=round_df.color)
可以看到,在6200次游戲時,已經(jīng)有11個玩家破產(chǎn),而這11個玩家到游戲結束時,也僅僅只有1個玩家能夠逆襲,剩下的10個玩家仍然處于破產(chǎn)狀態(tài)中。

3.努力的人生會更好嗎?
模型假設
在第二個模型的基礎上,我們增加一條規(guī)則:有10個人加倍努力,從而獲得了1%的競爭優(yōu)勢。
看看最終這10個人的情況。
Python模擬
同樣初始化數(shù)據(jù)集之后,100個玩家賦予不同的權重:
# 假設更努力的10個人ID為
luckier_players = [1, 11, 21, 31, 41, 51, 61, 71, 81, 91]
luckier_player_p = 0.01 * 1.01 # 更努力的人的概率
others_player_p = (1 - luckier_player_p * 10) / 90 # 其他人的概率
# 最終的概率列表
player_p = [luckier_player_p if i in luckier_players else others_player_p for i in players]
同樣進行17000輪游戲模擬,區(qū)別在于玩家獲得財富的概率不同:
result = [] # 存儲每次分配結果
total_round = 17000 # 總共輪次
# 保存還未開始游戲時每個玩家的的財富
result.append([0] + df['money'].to_list())
for round in range(1, total_round+1):
# 幸運鵝數(shù)量
lucky_guys_num = players_num
# 每個人的財富都減1
df['money'] = df['money'].apply(lambda x: x-1)
# 計算每個人增加的金額
lucky_guys = np.random.choice(players, size=lucky_guys_num, p=player_p) # 有多少個人-1,就抽取多少次,不同用戶賦予不同的權重
lucky_guys_bonus = Counter(lucky_guys) # 幸運鵝對應的獎勵金額
df['money'] = df.apply(lambda row: lucky_guys_bonus.get(row['player'], 0) + row['money'], axis=1)
result.append([round] + df['money'].to_list()) # 輪次以及每個玩家的財富
print(f'Round {round}')
# 所有財富分配的結果
result_df = pd.DataFrame(result, columns=["round"] + list(players))
查看所有玩家最后的財富排名,更努力的10個玩家同樣用紅色標出:
# 指定查看第幾輪的分配結果
round = 17000
# 指定輪次結果
round_df = result_df[result_df['round'] == round][players].T.reset_index()
round_df.columns = ['player', 'money']
# 更努力的10個人的顏色設置為紅色,其他人的顏色為藍色
round_df['color'] = round_df['player'].apply(lambda x: 'red' if x in luckier_players else 'blue')
# 按照財富從小到大排序
round_df.sort_values(by='money', ascending=True, inplace=True)
round_df.reset_index(drop=True, inplace=True)
# 財富分配情況
plt.figure(figsize=(20, 10))
plt.grid()
plt.bar(round_df.index, round_df.money, color=round_df.color)
社會財富的總體分布形態(tài)沒有什么變化,10個更加努力的玩家玩家中有6個進入了Top20:

再詳細看一下這10個玩家的財富變化情況:

小結
通過以上3個游戲的模擬,我們得出了以下的幾個結論:
- 最終的財富分布情況更接近于冪律分布(結論只是程序模擬,并非精確的數(shù)學求解),少數(shù)人掌握了大多數(shù)的財富;
- 35歲破產(chǎn)的人(對應著游戲中的6200次),到游戲結束時大多數(shù)仍然處于破產(chǎn)狀態(tài),只有極少數(shù)人能夠逆襲;
- 只需比其他人努力一點點,最終大概率能夠超過絕大多數(shù)人。
同樣的,大家也可以對游戲規(guī)則進行一定的修改,進行更多的模擬,例如富二代的情況會如何(初始資金大于100)?或者提出其他問題進行驗證亦可。
如果大家對本文的模擬感興趣的話,可以關注我的微信公眾號【活用數(shù)據(jù)】,回復【財富分配】即可獲得本文所有代碼的下載鏈接。