When people talk about the gender ratio imbalance in today's society, someone raised an issue: in some regions the policy was that after a couple had one child, if it was a girl they could have another child, but if it was a boy they had to stop. This, they argued, has caused the gender ratio imbalance. However, someone immediately objected that this policy's impact on the overall societal ratio is actually very small.
Argue is cheap, show me the code!
So I built a simple simulator to model this — just run it and see the results. The setup is as follows:
- Everyone starts looking for a partner at age 20, and lives until 60
- Newborns are male or female with a 1/2 probability each
- If a daughter is born, the couple tries again after two years; if a son is born, they stop (here I assume a one-year gap after each child before trying again)
- The couple also stops if they have five daughters (and of course they can't keep having children if one of them dies)
- Ideal conditions are assumed: older people pair up first, and no one tries anymore after age 45
- The number of people who never marry versus those who successfully marry
- The male-to-female ratio among the living
- The male-to-female ratio among the unmarried living
- The starting year is 2014
- At the start, the number of newborn males and females is equal
The simulator builds a few queues representing boys, girls, couples, and the "forever alone" folks, and tallies births, marriages, and deaths each year. The sex of a newborn is rolled with a random number, and each year it simply counts the information in each queue. The structure is very simple — the biggest headache for me was actually hand-implementing a linked queue.
So, do you think this policy has a big impact?
Appendix: Here's the relevant code and reference results directly — see if they match your expectations.
import random
from collections import deque
import matplotlib.pyplot as plt
from tqdm import tqdm
class Person:
"""表示一个人的类,包含性别、出生年份、婚姻状态等属性"""
def __init__(self, gender, birth_year):
self.gender = gender # 'M' for male, 'F' for female
self.birth_year = birth_year
self.married = False
self.spouse = None # 配偶引用
self.children = 0 # 孩子总数
self.last_child_year = None # 最后一个孩子出生的年份
self.daughters = 0 # 女儿数量
def age(self, current_year):
"""计算当前年龄"""
return current_year - self.birth_year
def is_alive(self, current_year):
"""判断是否在世(假设60岁为寿命上限)"""
return self.age(current_year) <= 60
def can_marry(self, current_year):
"""判断是否可以结婚(20-45岁之间且未婚)"""
return (
not self.married
and self.is_alive(current_year)
and self.age(current_year) >= 20
and self.age(current_year) < 45
)
def can_have_children(self, current_year):
"""判断是否可以生育孩子"""
# 必须已婚且双方都在世
if (
not self.married
or not self.is_alive(current_year)
or self.spouse is None # 防止spouse为None时访问is_alive方法
or not self.spouse.is_alive(current_year)
):
return False
# 如果已经有儿子或5个女儿,则停止生育
if self.children > 0 and self.daughters < self.children:
return False
if self.daughters >= 5:
return False
# 需要在上一个孩子出生后等待2年
if self.last_child_year is not None and current_year - self.last_child_year < 2:
return False
# 女性年龄限制(20-45岁)
if self.gender == "F" and (
self.age(current_year) < 20 or self.age(current_year) > 45
):
return False
return True
class Couple:
"""表示一对夫妻的类"""
def __init__(self, husband, wife, marriage_year):
self.husband = husband
self.wife = wife
self.marriage_year = marriage_year
# 更新双方的婚姻状态
husband.married = True
husband.spouse = wife
wife.married = True
wife.spouse = husband
def is_active(self, current_year):
"""判断夫妻双方是否都在世"""
return self.husband.is_alive(current_year) and self.wife.is_alive(current_year)
def can_have_children(self, current_year):
"""判断夫妻是否可以生育(委托给妻子判断)"""
return self.wife.can_have_children(current_year)
def have_child(self, current_year):
"""生育一个孩子,50%概率为男孩或女孩"""
# 50% chance for each gender
gender = "M" if random.random() < 0.5 else "F"
# 更新父母的孩子信息
self.husband.children += 1
self.wife.children += 1
self.wife.last_child_year = current_year
if gender == "F":
self.husband.daughters += 1
self.wife.daughters += 1
# 返回新生儿
return Person(gender, current_year)
class Simulation:
"""人口模拟类,模拟人口变化、婚姻和生育"""
def __init__(self, initial_population=1000, years=200, start_year=2014):
self.current_year = start_year
self.end_year = start_year + years
# 初始化人口队列
self.males = deque() # 未婚男性
self.females = deque() # 未婚女性
self.couples = deque() # 已婚夫妻
self.forever_alone_males = deque() # 超过45岁未婚男性
self.forever_alone_females = deque() # 超过45岁未婚女性
# 统计历史总人口
self.total_males_ever = 0
self.total_females_ever = 0
# 统计历史 forever alone
self.historical_forever_alone_males = 0
self.historical_forever_alone_females = 0
# 创建初始人口,男女比例相等
for _ in range(initial_population // 2):
# 随机年龄在0-60岁之间
male_birth = self.current_year - random.randint(0, 60)
female_birth = self.current_year - random.randint(0, 60)
self.males.append(Person("M", male_birth))
self.females.append(Person("F", female_birth))
self.total_males_ever += 1
self.total_females_ever += 1
# 统计数据
self.yearly_stats = []
def run(self):
"""运行模拟"""
for year in tqdm(
range(self.current_year + 1, self.end_year + 1), desc="Simulating years"
):
self.current_year = year
# 处理死亡
self.process_deaths()
# 处理婚姻
self.process_marriages()
# 处理生育
self.process_births()
# 收集统计数据
self.collect_stats()
self.display_results()
def process_deaths(self):
"""处理人口死亡(移除超过60岁的人)"""
# 移除已经达到寿命上限的人
self.males = deque([m for m in self.males if m.is_alive(self.current_year)])
self.females = deque([f for f in self.females if f.is_alive(self.current_year)])
self.forever_alone_males = deque(
[m for m in self.forever_alone_males if m.is_alive(self.current_year)]
)
self.forever_alone_females = deque(
[f for f in self.forever_alone_females if f.is_alive(self.current_year)]
)
# 更新夫妻列表,只保留双方都在世的夫妻
active_couples = deque()
for couple in self.couples:
if couple.is_active(self.current_year):
active_couples.append(couple)
self.couples = active_couples
def process_marriages(self):
"""处理婚姻匹配,年龄大的优先"""
# 按年龄排序(年龄大的优先)
marriageable_males = sorted(
[m for m in self.males if m.can_marry(self.current_year)],
key=lambda x: self.current_year - x.birth_year,
reverse=True,
)
marriageable_females = sorted(
[f for f in self.females if f.can_marry(self.current_year)],
key=lambda x: self.current_year - x.birth_year,
reverse=True,
)
# 匹配夫妻
while marriageable_males and marriageable_females:
husband = marriageable_males.pop(0)
wife = marriageable_females.pop(0)
# 从单身池中移除
if husband in self.males:
self.males.remove(husband)
if wife in self.females:
self.females.remove(wife)
# 创建新的夫妻
self.couples.append(Couple(husband, wife, self.current_year))
# 将超过45岁的人加入"永远单身"列表
for m in list(self.males):
if m.age(self.current_year) >= 45 and not m.married:
self.males.remove(m)
self.forever_alone_males.append(m)
self.historical_forever_alone_males += 1
for f in list(self.females):
if f.age(self.current_year) >= 45 and not f.married:
self.females.remove(f)
self.forever_alone_females.append(f)
self.historical_forever_alone_females += 1
def process_births(self):
"""处理生育"""
for couple in self.couples:
if couple.can_have_children(self.current_year):
child = couple.have_child(self.current_year)
if child.gender == "M":
self.males.append(child)
self.total_males_ever += 1
else:
self.females.append(child)
self.total_females_ever += 1
def collect_stats(self):
"""收集年度统计数据"""
# 计算总人口
total_males = (
len(self.males)
+ len(self.forever_alone_males)
+ sum(1 for c in self.couples if c.husband.is_alive(self.current_year))
)
total_females = (
len(self.females)
+ len(self.forever_alone_females)
+ sum(1 for c in self.couples if c.wife.is_alive(self.current_year))
)
# 计算未婚人口
unmarried_males = len(self.males) + len(self.forever_alone_males)
unmarried_females = len(self.females) + len(self.forever_alone_females)
# 存储统计数据
self.yearly_stats.append(
{
"year": self.current_year,
"total_males": total_males,
"total_females": total_females,
"unmarried_males": unmarried_males,
"unmarried_females": unmarried_females,
"couples": len(self.couples),
"forever_alone_males": len(self.forever_alone_males),
"forever_alone_females": len(self.forever_alone_females),
}
)
def display_results(self):
"""显示最终结果"""
final_stats = self.yearly_stats[-1]
print("n=== Final Statistics ===")
print(f"Year: {final_stats['year']}")
print(
f"Total population: {final_stats['total_males'] + final_stats['total_females']}"
)
print(
f"Gender ratio (M:F): {final_stats['total_males']} : {final_stats['total_females']} = {final_stats['total_males'] / final_stats['total_females']:.2f}"
)
print(
f"Unmarried gender ratio (M:F): {final_stats['unmarried_males']} : {final_stats['unmarried_females']} = {final_stats['unmarried_males'] / final_stats['unmarried_females'] if final_stats['unmarried_females'] > 0 else 'inf':.2f}"
)
print(f"Forever alone males: {final_stats['forever_alone_males']}")
print(f"Forever alone females: {final_stats['forever_alone_females']}")
print(f"Active couples: {final_stats['couples']}")
# 新增:输出历史总人口及比例
print("n--- Historical Population (All Ever Born) ---")
print(f"Total males ever born: {self.total_males_ever}")
print(f"Total females ever born: {self.total_females_ever}")
if self.total_females_ever > 0:
print(f"Historical gender ratio (M:F): {self.total_males_ever} : {self.total_females_ever} = {self.total_males_ever / self.total_females_ever:.2f}")
else:
print("Historical gender ratio (M:F): inf")
print(f"Historical Forever Alone Males: {self.historical_forever_alone_males}")
print(f"Historical Forever Alone Females: {self.historical_forever_alone_females}")
# 绘制性别比例随时间变化的图表
self.plot_results()
def plot_results(self):
"""绘制结果图表"""
years = [stat["year"] for stat in self.yearly_stats]
gender_ratios = [
(
stat["total_males"] / stat["total_females"]
if stat["total_females"] > 0
else 0
)
for stat in self.yearly_stats
]
unmarried_ratios = [
(
stat["unmarried_males"] / stat["unmarried_females"]
if stat["unmarried_females"] > 0
else 0
)
for stat in self.yearly_stats
]
plt.figure(figsize=(12, 6))
# 绘制总体性别比例图
plt.subplot(1, 2, 1)
plt.plot(years, gender_ratios, "b-", label="Gender Ratio (M:F)")
plt.axhline(y=1.0, color="r", linestyle="--", label="Equal Ratio")
plt.title("Gender Ratio Over Time")
plt.xlabel("Year")
plt.ylabel("Ratio (M:F)")
plt.legend()
# 绘制未婚人口性别比例图
plt.subplot(1, 2, 2)
plt.plot(years, unmarried_ratios, "g-", label="Unmarried Ratio (M:F)")
plt.axhline(y=1.0, color="r", linestyle="--", label="Equal Ratio")
plt.title("Unmarried Gender Ratio Over Time")
plt.xlabel("Year")
plt.ylabel("Ratio (M:F)")
plt.legend()
plt.tight_layout()
plt.savefig("gender_ratio_simulation.png")
plt.show()
if __name__ == "__main__":
# 运行模拟
sim = Simulation(initial_population=10000, years=200, start_year=2014)
sim.run()
参考结果:
=== Final Statistics ===
Year: 2214
Total population: 14688
Gender ratio (M:F): 7330 : 7358 = 1.00
Unmarried gender ratio (M:F): 2383 : 2411 = 0.99
Forever alone males: 0
Forever alone females: 0
Active couples: 4947
--- Historical Population (All Ever Born) ---
Total males ever born: 32943
Total females ever born: 32884
Historical gender ratio (M:F): 32943 : 32884 = 1.00
Historical Forever Alone Males: 1354
Historical Forever Alone Females: 1263
Comments · 19