网上谈论到当今社会男女比例失调,有人说到一个问题,某些地方政策是,夫妻俩生育一个孩子后,如果是女的就可以再生一个,如果是男的,就不能继续了。这导致了男女比例失调。 然而,立马有人反对说,这件事对全社会影响比例非常小。
Argue is cheap, show me the code!
我做了一个简单的模拟器,模拟这个事情,看结果就完了。 具体设定如下:
- 每个人到20岁开始寻找伴侣,到60生命结束
- 新生儿男女比例都是1/2
- 生下女儿则两年后继续尝试,生下儿子则停止尝试(在此,我假定生育完一个孩子后要隔一年再来战过。)
- 如果生下五个女儿,也停止尝试(当然,如果夫妻有人死了自然不能继续生育了)
- 假定理想条件,年纪大优先找到另一半,但45岁后不再尝试
如此模拟200年(这个时间我随便写的,修改个变量就是2000年也很容易),输出:
- 终身未娶/未嫁和成功完婚的数量
- 在世的男女比例
- 在世未婚的男女比例
为了代码方便,使用了这样一些假定如下以hard code放里面了。
- 假定开始时间是2014年
- 开始时刚出生的男女人数相同
模拟器构造了几个队列,分别表示男孩,女孩,夫妻和FA的筒子们,每年对生孩子,结婚和死亡分别统计。生下的孩子性别用随机数roll个,每年统计下各个队列里面的信息即可。构造非常简单,它对我的最大困扰其实是自己手工实现个链式队列。
那么,你觉得这个政策影响大么?
附:直接给一个相关代码和参考结果吧,你看符合你预期吧。
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
19 Comments
reizhi · March 27, 2014 at 23:50
感觉不能这么无限制生下去,无论是男是女最多2胎
yu · March 28, 2014 at 10:12
@reizhi 我那个模拟器模拟上几千年后,结果是人类灭绝的。
因为两个人生下子代个数的期望E = 1 * 1/2 + 2 * 1/4 + 3 * 1/8 + 4 * 1 / 16 + 5 * 1 / 16 约为1.93 < 2 所以从趋势上说是整体下滑的。 而现实中,即便每家都可以生两胎,依然会整体下滑,比如意外啦,比如不想要两个孩子,乃至一个都不要的,都是可能的。 为什么还有人口膨胀的现象呢?因为当无法给子代良好的环境,却以为&期望基因多组合几次,能多弄几个份额就好 -- 就好像猪肉,不好吃没太大问题,抗不住它量大。 所以无论是超生还是计生,个人觉得都没太多用,没有良好的教育环境,没有舒适可靠的生活,人命就会变贱,膨胀自然而然,反之则反之。
reizhi · March 28, 2014 at 14:11
@Yu Jing 果然是死理性派
海蓝 · March 6, 2014 at 10:00
难道不是因为重男轻女才导致失衡的吗
yu · March 6, 2014 at 11:41
@海蓝 造成男女失衡的原因可以有很多,只是说,从某个角度看,是否可能造成比较大的影响。这只是个数学题目而已。
至于具体是什么造成比例失衡什么的,这个是人文类问题,我就不去抢别人的话筒了
海蓝 · May 8, 2014 at 17:15
@Yu Jing 好吧 原来是技术派
orbea jersey · March 5, 2014 at 10:42
哈哈,楼主想得真多,现在的社会本来就男女比例失调了啊
rock racing cyclisme · February 26, 2014 at 10:33
有的也是家庭因素,有重男轻女的现在,才导致失衡。
爱浮夸 · February 21, 2014 at 18:09
感觉这东西还是交给天去算吧。
Leniy · February 21, 2014 at 08:07
第一队列,生一胎的。男女概率均等。
生二胎的,男女概率均等。
Leniy · February 21, 2014 at 08:07
@Leniy 只要不出生前根据性别堕胎,那么每一次出生均是独立事件。男女概率不受影响。
生男生女是均等事件,控制谁可以生二胎是另一个事件,二者不相关。
yu · February 21, 2014 at 12:22
@Leniy 赞
当时说这问题的是某个博士生,我也没当时就搞明白。。(好水)
Vespa · February 21, 2014 at 13:24
@Yu Jing 我多年前在中学生读物上看到过这个东西。。
yu · February 21, 2014 at 13:27
@Vespa 我读书少了….
小时候政治课就看到这个题目的,一直没有思考过以为是正确的,寒假才正儿八经的数学上考虑这个是否正确。。囧
Vespa · February 21, 2014 at 13:33
@Yu Jing 不过这种问题仿真我是绝对不会用C/C++来做的,只是验证一个东西付出的代码量太大了。。
yu · February 21, 2014 at 15:01
@Vespa 其实就是几个队列而已,当时寒假在家没有网啥都干不了,熟悉下手感而已
Leniy · February 23, 2014 at 08:24
@Vespa 用来练手还是不错的
Vespa · February 23, 2014 at 16:46
@Yu Jing 模型假设是不是有点冗余。。有些假设并不涉及问题本质。所以才会出现队列这种数据结构。。
yu · February 23, 2014 at 17:04
@Vespa 对”这样是否会导致男女比例失衡?”这个问题本身说,我的确多统计了一些信息。
不过在写的时候,当时还有一些其它语境,比如”终身未娶/未嫁和成功完婚的数量”,因为我当时本想嘲讽某人”这政策的推广是你Forever Alone的重要原因之一”(好吧,然后自己算了下不得不思考当时为啥脑子没了)