2012年2月,博主 @奥卡姆剃刀 发表了一条微博

某国生男生女比例为1:1,国王为了抵御外敌入侵下令城民头胎生男者可续生,生女者禁生,几十年后,该国的男女比例能增加吗?

按照常理,这样的政策执行应该不会对人口比例造成影响,然而教主 @tombkeeper 却经过枚举验证,得到了不一样的结论

微博截图

教主的验证代码由C语言编写,由于我尚未学习C语言,只能向ChatGPT求助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
\```C
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MALE 1
#define FEMALE 0

void main() {
int s;
int i;
int m = 0;
int f = 0;

srand((unsigned)time(NULL));
//一百万75%的概率生男孩的家庭
for (i = 0 ; i < 1000000 ; i++) {
s = rand() %2 | rand() %2;
if (s == MALE) {
m++;
//若头胎男孩再生一胎
s = rand() %2 | rand() %2;
if (s == MALE) {
m++;
} else {
f++;
}
} else {
f++;
}
}

//一百万75%的概率生女孩的家庭
for (i = 0 ; i < 1000000 ; i++) {
s = rand() %2 & rand() %2;
if (s == MALE){
m++;
//若头胎男孩再生一胎
s = rand() %2 & rand() %2;
if (s == MALE) {
m++;
} else {
f++;
}
} else {
f++;
}
}

printf("MALE:FEMALE = %d:%d\n", m, f);
}
\```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MALE 1
#define FEMALE 0

int main() {
int s;
int i;
int m = 0;
int f = 0;

srand((unsigned)time(NULL));
// 一百万75%的概率生男孩的家庭
for (i = 0 ; i < 1000000 ; i++) {
s = (rand() % 4) < 3 ? MALE : FEMALE; // 75% 的机会是 MALE
if (s == MALE) {
m++;
} else {
f++;
}
}

// 一百万75%的概率生女孩的家庭
for (i = 0 ; i < 1000000 ; i++) {
s = (rand() % 4) < 3 ? FEMALE : MALE; // 75% 的机会是 FEMALE
if (s == MALE){
m++;
} else {
f++;
}
}

printf("MALE:FEMALE = %d:%d\n", m, f);

return 0;
}

上述代码中体现了这些知识点:

  1. 计算随机数的方法

    在C语言中,rand() 函数返回的是一个随机整数,范围是从0到RAND_MAX,其中 RAND_MAX 是一个预定义的常数,在大多数系统上,它的值是32767。

  2. 按位运算

    计算机的数值计算的底层逻辑,可以提高计算速度。它并不特定于任何一种语言,C和Java都支持位运算,并且它们的效率都是比较高的。但在这个特定的问题中,我们并不需要使用位运算,因为我们不是在处理位级别的操作,我们只是生成随机数并进行比较。

  3. 对概率的模拟

    ChatGPT认为:rand() % 2 生成的是0或1,这意味着有50%的概率得到0或1。代码中使用了按位或(|)和按位与(&)操作符,实际上并没有改变这个概率。

  4. 蒙特卡罗算法

    也称统计模拟方法,使用随机数(或更常见的伪随机数)来解决很多计算问题的方法。 将所求解的问题同一定的概率模型相联系,用电子计算机实现统计模拟或抽样,以获得问题的近似解。

同时,ChatGPT也使用Java语言重写了上面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class GenderRatio {

static final int MALE = 1;
static final int FEMALE = 0;

public static void main(String[] args) {
int s;
int m = 0;
int f = 0;

// 一百万75%的概率生男孩的家庭
for (int i = 0; i < 1000000; i++) {
s = ((int) (Math.random() * 4) < 3) ? MALE : FEMALE;
if (s == MALE) {
m++;
s = ((int) (Math.random() * 4) < 3) ? MALE : FEMALE;
if (s == MALE) {
m++;
} else {
f++;
}
} else {
f++;
}
}

// 一百万75%的概率生女孩的家庭
for (int i = 0; i < 1000000; i++) {
s = ((int) (Math.random() * 4) < 3) ? FEMALE : MALE;
if (s == MALE) {
m++;
s = ((int) (Math.random() * 4) < 3) ? FEMALE : MALE;
if (s == MALE) {
m++;
} else {
f++;
}
} else {
f++;
}
}

System.out.printf("MALE:FEMALE = %d:%d\n", m, f);
}
}

在这个代码中,(int) (Math.random() * 4) 生成了一个0到3的随机整数。然后我们比较这个数是否小于3来决定性别。如果是,则有75%的机会是男孩(或女孩,在第二个循环中)。这里我们没有使用位运算,因为在这个情况下,位运算并不提供任何实际的好处,并且可能使代码更难理解。

从数学逻辑上对结果进行分析:

代码中设定的条件不是完全随机。当前代码在两个循环中分别模拟了两种情况:

  1. 第一个循环中,有75%的概率生男孩的家庭。
  2. 第二个循环中,有75%的概率生女孩的家庭。

在两个不同的情况中,都没有严格遵循自然界的男女出生概率。

也就是说,教主在微博评论区中提到的“两百多年来只诞生过两位女性”的家庭,也是自然界对真正的随机情况(即,每个孩子出生都是独立事件,且男孩和女孩的出生概率相等)的一种补充,有的家庭中女孩有更高的出生率,来平衡这种男性单传的现象。

再换一个角度看,也许有的人在赌博中连赢,也是在数学概率上有些人爆亏来平衡比率的结果,这样想是不是有点走向宿命论了?