这是第七个实验。第 25 讲,我们用一个公式算出了渔场的悲剧:开放进入时,出海的船太多,租金被耗散到零。第 25 讲还留了一个承诺:用模拟看共用的资源怎样一步步被耗散,以及加入监督和规则之后会怎样。 这是这个实验的第一部分。

第二部分,换一个看上去完全不同的题目:Schelling 的隔离模型。一个城市里住着两种人,每个人只有一个很温和的要求:邻居里,至少有一部分和自己同类。结果呢?这两部分放在一起,是因为它们讲的是同一件事:每个人的选择都很合理,合起来的结果,却是谁也没有想到、也没有想要的。 用模拟,你可以亲眼看到,这个"合起来"是怎样一步一步发生的。

怎么用:和前面的实验一样,代码块是接着往下写的:后面的块会用到前面块里定义的东西。把它们依次拼成一个文件(比如 lab76.py),用 python lab76.py 运行,约 40 秒;也可以一段一段地在 Python 里依次运行。只需要 Python 3 和 NumPy。实验里有随机数,但种子都固定了,所以你跑出来的数字,会和下面的输出一模一样。

一、六个问题

先停一下:请先凭直觉,回答下面六个问题,写下来。

问题 1: 一片没有人管的渔场,谁都可以来。从一片没人动过的渔场出发,出海的船会一路增加,直到不赚钱为止。过程中,鱼的存量会平稳地降到那个水平吗?

问题 2: 如果整片渔场只有一个主人,他会出海多少?如果他只看自己每条船赚不赚钱,不去想"多出一条船,会让所有船都少捞鱼",又会怎样?

问题 3: 禁渔十年,鱼的存量会完全恢复。然后重新开放,会怎样?

问题 4: 十个渔民约好,每人出海一个规定的量。每个人都可以偷偷多出海,被巡查发现的概率是 q,发现了就停捕 k 年。q 要多大、k 要多长,才管得住?

问题 5: 一个城市里,两种人各占一半,每个人只要求"邻居里至少 30% 和我同类",换句话说,接受自己是 70% 对 30% 的少数。最后,邻居里同类的平均比例,会是多少?

问题 6: 如果只有少数人有这样的要求,其他人对邻居完全无所谓,最后的隔离,会比所有人都有要求时轻很多吗?

我们会一个一个回来。

二、实验的设计

2.1 第一部分:一片会长鱼的渔场

第 25 讲的渔场,只有一个公式:总渔获 H(E) = 10E − E²。这个公式,其实是一片会自己长鱼的渔场,稳定下来之后的样子。现在,我们把"长鱼"这件事,放进模拟里:

dS/dt = r S (1 − S/K) − q E S

S 是鱼的存量,没人捕的时候,它按 logistic 的方式自己长(增长率 r,最多长到 K);每单位出海努力量 E,每年捕走存量的 q 比例。渔获的价格是 1,每单位努力的成本是 c。我们取 K = 100,r = 1,q = 0.1,c = 4:努力量固定不变、鱼的存量稳定下来之后,渔获正好是 10E − E²,和第 25 讲一样。

这样的模型,叫 Gordon–Schaefer 模型:Schaefer(1954)把 logistic 增长放进渔业,Gordon(1954)加上了经济学(第 25 讲引过)。

2.2 第二部分:一个格子上的城市

一个 40 × 40 的格子(边界首尾相连),格子里住着红、蓝两种人,各占 45%,另有 10% 的格子是空的。每个人的"偏好"只有一条:周围 8 个邻居里,和自己同类的人,占已住人的比例,不低于 tau,就满意;否则,想搬走。想搬的人,搬到一个随机的、搬过去会满意的空格子里;找不到这样的格子,就留在原地。一轮一轮,直到没有人搬,或者 200 轮到了。

这是 Thomas Schelling 1971 年(Journal of Mathematical Sociology,1 卷 2 期,143–186 页)提出的模型。

三、第一步:稳定之后的渔场,和开放进入的过程

import numpy as np

# 一片渔场:鱼的存量 S 自己会长(logistic),也会被捕。出海努力量 E(船的总数),每单位努力捕走存量的 q 比例。
#   dS/dt = r S (1 - S/K) - q E S           (自己长的,减去被捕的)
# 渔获的价格是 1,每单位努力的成本是 c。参数取得和第 25 讲的 H(E) = 10E - E² 一致:K = 100,r = 1,q = 0.1,c = 4。
K, R, Q, PRICE, C = 100.0, 1.0, 0.1, 1.0, 4.0

def steady_state(E):
    """努力量 E 固定不变,等鱼的存量稳定下来:存量 S*,每期渔获 H,总租金(渔获的价值 - 成本)"""
    S = max(0.0, K * (1 - Q * E / R))
    H = Q * E * S
    return S, H, PRICE * H - C * E

print("努力量 E 固定不变时,稳定下来的渔场(公式 H(E) = 10E - E²,和第 25 讲一样):")
print("   E    鱼的存量 S*   每期渔获 H   总租金")
for E in (0, 1, 2, 3, 4, 5, 6, 7, 8, 10):
    S, H, rent = steady_state(E)
    print(f"  {E:>2}   {S:9.1f}   {H:10.1f}   {rent:7.1f}")

Es = np.linspace(0, 10, 10001)
rents = np.array([steady_state(E)[2] for E in Es]); yields = np.array([steady_state(E)[1] for E in Es])
E_mey, E_msy = Es[rents.argmax()], Es[yields.argmax()]
E_open = Es[(rents >= -1e-9) & (Es > 0)].max()
print(f"\n租金最大的努力量 E = {E_mey:.2f}(租金 {rents.max():.1f},鱼的存量 {steady_state(E_mey)[0]:.0f});")
print(f"渔获最大的努力量 E = {E_msy:.2f}(渔获 {yields.max():.1f},租金 {steady_state(E_msy)[2]:.1f},鱼的存量 {steady_state(E_msy)[0]:.0f});")
print(f"开放进入(租金耗散到 0)的努力量 E = {E_open:.2f}(鱼的存量 {steady_state(E_open)[0]:.0f},渔获 {steady_state(E_open)[1]:.1f})。")

# 开放进入,一年一年地走:谁都能进,有利可图就有人进来,亏钱就有人退出。可是造船、招人都要时间:努力量的调整,看的是 lag 年以前的利润。
def run_fishery(E0=0.5, S0=100.0, speed=0.5, lag=0.0, years=120, dt=0.1, cost=C, cap=None, forced=None):
    """欧拉法,步长 dt 年。每单位努力的利润 = 价格 × q × S - cost。努力量按 speed × (lag 年前的利润) 调整。
    cap:努力量的上限(配额);forced:{年份区间: 努力量},把努力量强行定住(比如禁渔)。返回数组:每行 (年, S, E, 渔获, 总利润)"""
    S, E, prof, path, lag_steps = S0, E0, [], [], int(round(lag / dt))
    for step in range(int(years / dt) + 1):
        t = step * dt
        if forced:
            for (lo, hi), val in forced.items():
                if lo <= t < hi: E = val
        path.append((t, S, E, PRICE * Q * E * S, PRICE * Q * E * S - cost * E))
        prof.append(PRICE * Q * S - cost)
        seen = prof[max(0, len(prof) - 1 - lag_steps)]
        S = max(S + dt * (R * S * (1 - S / K) - Q * E * S), 1e-6)
        E = max(E + dt * speed * seen, 0.0)
        if cap is not None: E = min(E, cap)
    return np.array(path)

print("\n开放进入,从一片没人动过的渔场(S = 100)出发。努力量调整的速度 speed、时滞 lag 不同(稳定的 E = 6,S = 40):")
print("  速度   时滞(年)   努力量最高冲到   存量最低跌到   最后 30 年,E 的范围")
paths = {}
for speed, lag in ((0.2, 0), (0.5, 0), (0.5, 1), (0.5, 2), (0.5, 3), (1.0, 1)):
    p = run_fishery(speed=speed, lag=lag); paths[(speed, lag)] = p; tail = p[-300:]
    print(f"  {speed:>4}   {lag:>6}   {p[:, 2].max():12.2f}   {p[:, 1].min():12.1f}   {tail[:, 2].min():5.2f} 到 {tail[:, 2].max():5.2f}")

输出:

努力量 E 固定不变时,稳定下来的渔场(公式 H(E) = 10E - E²,和第 25 讲一样):
   E    鱼的存量 S*   每期渔获 H   总租金
   0       100.0          0.0       0.0
   1        90.0          9.0       5.0
   2        80.0         16.0       8.0
   3        70.0         21.0       9.0
   4        60.0         24.0       8.0
   5        50.0         25.0       5.0
   6        40.0         24.0       0.0
   7        30.0         21.0      -7.0
   8        20.0         16.0     -16.0
  10         0.0          0.0     -40.0

租金最大的努力量 E = 3.00(租金 9.0,鱼的存量 70);
渔获最大的努力量 E = 5.00(渔获 25.0,租金 5.0,鱼的存量 50);
开放进入(租金耗散到 0)的努力量 E = 6.00(鱼的存量 40,渔获 24.0)。

开放进入,从一片没人动过的渔场(S = 100)出发。努力量调整的速度 speed、时滞 lag 不同(稳定的 E = 6,S = 40):
  速度   时滞(年)   努力量最高冲到   存量最低跌到   最后 30 年,E 的范围
   0.2        0           6.10           39.4    6.00 到  6.00
   0.5        0           7.17           34.0    6.00 到  6.00
   0.5        1           9.26           21.7    6.00 到  6.00
   0.5        2          12.10            8.3    3.10 到  9.01
   0.5        3          15.09            1.6    0.00 到 14.24
   1.0        1          14.11            8.1    0.00 到 12.00

左:努力量固定时,渔获和租金;右:开放进入下,鱼的存量,有无时滞

逐段看:

  • 三个不同的"最好":

    • 租金最大:E = 3,鱼的存量 70,租金 9。
    • 渔获最大(“最大可持续渔获量”):E = 5,渔获 25,租金只有 5,鱼的存量 50。
    • 开放进入:E = 6,鱼的存量 40,租金 0。

    “能捞最多鱼"和"社会最优”,是两回事:多出来的 2 单位努力,只多捞到 4 条鱼(25 − 21),却多花 8 的成本。

  • 开放进入的过程。努力量按利润来调整:赚钱就增加,亏钱就减少,速度是 speed。没有时滞(船马上造好),速度 0.5:努力量冲到 7.17,鱼的存量最低跌到 34.0,然后稳定在 E = 6、S = 40。

  • 有时滞就会一轮轮地过头:造船、招人,都要时间,所以努力量调整时,看的是 lag 年以前的利润:

    • lag = 1 年:E 冲到 9.26,S 跌到 21.7,最后还是稳定下来;
    • lag = 2 年:E 冲到 12.10,S 最低 8.3,不再稳定,而是循环,最后 30 年,E 在 3.1 到 9.0 之间来回;
    • lag = 3 年:S 最低 1.6(几乎没了),E 在 0 到 14.24 之间来回。

问题 1 的答案:不会。 在没有时滞的最简单情形下,存量会冲过 40,跌到 34,再回来;有一点时滞,它就会一轮一轮地大幅摆动,甚至跌到几乎没有。开放进入,不只是"稳定在不赚钱的水平",还可能是"沿途把资源吃到濒危"。 渔场的崩溃,原因有很多(第 25 讲提到的 Costello 等人的数据,也只是说管理办法和崩溃的比例有关);这里的"冲过头",只是其中一种可能的机制。

四、第二步:一个人想的,和一群人想的

渔民不止一个。n 个渔民,每人自己决定自己的努力量 e_i(总努力量 E = Σ e_i)。我们比较三种"想法":

  • A「看平均利润」:我这一年每单位努力赚钱,就多出海一点;亏钱,就少出海一点。(我看得见自己的利润,看不见自己对大家的影响。)
  • B「算边际」:我知道,多出一条船,会把每一条船的渔获都拉低,这个损失,我要把它算进去。在别人的努力量不变的前提下,选对我最好的 e_i。这就是第 25 讲的纳什均衡。
  • C「合作」:大家商量好,把总努力量定在让总租金最大的 E = 3,平分。
# 第二步:渔民不止一个。n 个渔民,每人自己决定自己的努力量 e_i(总努力量 E = Σ e_i)。三种"想法":
#   A「看平均利润」:自己这一年每单位努力赚钱就多出海一点,亏钱就少出海一点(看得见自己的利润,看不见自己对大家的影响)
#   B「算边际」:知道自己多出海一点,会把每一艘船的渔获都拉低,把这个算进去,在别人不变的前提下,选对自己最好的 e_i(第 25 讲的纳什均衡)
#   C「合作」:大家商量好,把总努力量定在让总租金最大的 E = 3,平分
def nash_effort(n):
    """B:每人的最好反应 e_i = (10 - c - 别人的总和) ÷ 2,反复调整直到不变"""
    e = np.full(n, 1.0)
    for _ in range(3000):
        for i in range(n): e[i] = max(0.0, (10 - C - (e.sum() - e[i])) / 2)
    return e

def adaptive_fishers(n, speed=0.5, years=300, dt=0.1):
    """A:n 个渔民,各自按自己的利润调整,鱼的存量照常增长和被捕。返回最后的 E、S"""
    S, e = 100.0, np.full(n, 0.5 / n)
    for _ in range(int(years / dt)):
        pe = PRICE * Q * S - C
        S = max(S + dt * (R * S * (1 - S / K) - Q * e.sum() * S), 1e-6)
        e = np.maximum(e + dt * speed * pe / n, 0.0)               # 每个人的调整,和总努力量的调整,一样快(除以 n 是为了总速度相同)
    return e.sum(), S

print("n 个渔民,三种想法下,长期的总努力量 E、鱼的存量 S、总租金(每年):")
print("   n   A 看平均利润:E    S    租金   |  B 算边际:E    S    租金   |  C 合作:E    S    租金")
table = {}
for n in (1, 2, 3, 5, 10, 100):
    EA, SA = adaptive_fishers(n); rA = PRICE * Q * EA * SA - C * EA
    EB = nash_effort(n).sum(); SB, _, rB = steady_state(EB)
    SC, _, rC = steady_state(3.0)
    table[n] = (EA, SA, rA, EB, SB, rB)
    print(f"  {n:>3}        {EA:5.2f}  {SA:5.1f}  {rA:5.2f}   |        {EB:5.2f}  {SB:5.1f}  {rB:5.2f}   |       3.00  {SC:5.1f}  {rC:5.2f}")

输出:

n 个渔民,三种想法下,长期的总努力量 E、鱼的存量 S、总租金(每年):
   n   A 看平均利润:E    S    租金   |  B 算边际:E    S    租金   |  C 合作:E    S    租金
    1         6.00   40.0   0.00   |         3.00   70.0   9.00   |       3.00   70.0   9.00
    2         6.00   40.0   0.00   |         4.00   60.0   8.00   |       3.00   70.0   9.00
    3         6.00   40.0   0.00   |         4.50   55.0   6.75   |       3.00   70.0   9.00
    5         6.00   40.0   0.00   |         5.00   50.0   5.00   |       3.00   70.0   9.00
   10         6.00   40.0   0.00   |         5.45   45.5   2.98   |       3.00   70.0   9.00
  100         6.00   40.0   0.00   |         5.94   40.6   0.35   |       3.00   70.0   9.00

三种想法下,长期的总努力量(左)和总租金(右)

逐段看:

  • A:不管几个人,E 都是 6,租金都是 0。 包括 n = 1:一个主人,如果只看"自己的船赚不赚钱",也会把整片渔场,一路开发到租金耗散。
  • B:算边际,n = 1 是 3(和合作一样),人越多,越接近开放进入。 E = 3、4、4.5、5、5.45、5.94(n = 1、2、3、5、10、100),租金 9、8、6.75、5、2.98、0.35,和第 25 讲一致。
  • C:合作,E = 3,租金 9,存量 70。

问题 2 的答案:一个主人,会算边际,出海 3;只看平均利润,出海 6,和开放进入一样。所以:问题不只是"人太多",也不只是"没有主人",而是"每个人看到的,是不是自己行为的全部后果"。 一个主人,如果只看平均利润,同样会把渔场耗尽;而 n 个渔民,如果每个人都"算边际",也不等于合作:人越多,每个人只承担自己行为的 1/n 的代价,越接近开放进入。

(这里的 A 和 B,是两种行为模型,不是对现实渔民的描述。)

五、第三步:几种管理办法

同一片渔场,同样的渔民(speed = 0.5,lag = 1 年),120 年,四种管理:不管;对每单位努力征税 3;可转让配额,总努力量不超过 3;禁渔 10 年(第 10 年到第 20 年),之后重新开放。

# 第三步:办法。同一片渔场,同样的渔民(speed = 0.5,lag = 1 年),120 年,四种管理:
#   不管(开放进入);每单位努力征税 3;可转让配额(总努力量不超过 3);禁渔 10 年(第 10 年到第 20 年),之后重新开放。
def summarize(p, label, extra_rev=0.0):
    t = p[:, 0]; late = t >= 60
    print(f"  {label:<22} 努力量 {p[late, 2].mean():5.2f}   鱼的存量 {p[late, 1].mean():5.1f}   渔民的租金 {p[late, 4].mean():5.2f}   税收 {extra_rev:5.2f}   渔获 {p[late, 3].mean():5.2f}")

print("后 60 年(第 60 到 120 年)的年平均:")
base = run_fishery(speed=0.5, lag=1)
tax = run_fishery(speed=0.5, lag=1, cost=C + 3.0)
cap = run_fishery(speed=0.5, lag=1, cap=3.0)
closed = run_fishery(speed=0.5, lag=1, forced={(10, 20): 0.0})
summarize(base, "不管(开放进入)")
summarize(tax, "征税(每单位努力 3)", extra_rev=(3.0 * tax[tax[:, 0] >= 60, 2]).mean())      # 征税时,渔民的成本是 c + 3,所以这一列已经是扣完税以后的租金
summarize(cap, "可转让配额(E ≤ 3)")
summarize(closed, "禁渔 10 年,之后开放")

late_closed = closed[closed[:, 0] >= 20]
print(f"\n禁渔那 10 年:第 10 年 S = {closed[closed[:, 0] == 10][0, 1]:.1f},第 20 年(重新开放时)S = {closed[closed[:, 0] == 20][0, 1]:.1f};")
print(f"重新开放之后:努力量冲到 {late_closed[:, 2].max():.2f},鱼的存量又跌到最低 {late_closed[:, 1].min():.1f}(不禁渔的那条:努力量最高 {base[:, 2].max():.2f},存量最低 {base[:, 1].min():.1f}),")
print(f"到第 60 年以后,和不禁渔时一模一样:S = {closed[closed[:, 0] >= 60, 1].mean():.1f},租金 {closed[closed[:, 0] >= 60, 4].mean():.2f}。")

输出:

后 60 年(第 60 到 120 年)的年平均:
  不管(开放进入)               努力量  6.00   鱼的存量  40.0   渔民的租金  0.00   税收  0.00   渔获 24.01
  征税(每单位努力 3)            努力量  3.00   鱼的存量  70.0   渔民的租金  0.00   税收  9.00   渔获 21.00
  可转让配额(E ≤ 3)           努力量  3.00   鱼的存量  70.0   渔民的租金  9.00   税收  0.00   渔获 21.00
  禁渔 10 年,之后开放           努力量  5.99   鱼的存量  40.0   渔民的租金  0.02   税收  0.00   渔获 23.99

禁渔那 10 年:第 10 年 S = 27.6,第 20 年(重新开放时)S = 100.0;
重新开放之后:努力量冲到 9.25,鱼的存量又跌到最低 21.8(不禁渔的那条:努力量最高 9.26,存量最低 21.7),
到第 60 年以后,和不禁渔时一模一样:S = 40.0,租金 0.02。

四种管理办法下,鱼的存量(左)和出海努力量(右);紫色带是禁渔的 10 年

逐段看:

  • 征税 3:E 稳定在 3,鱼的存量 70;渔民的租金是 0(税把租金全拿走了),政府收 9。
  • 可转让配额(E ≤ 3):E 稳定在 3,存量 70;租金 9,留在拿到配额的渔民手里。
  • 同样的 E = 3,同样的存量,差别只在谁拿租金:政府(税),还是渔民(配额)。这是第 23 讲"税和排污权,效率相同,分配不同"在渔场里的样子。
  • **禁渔 10 年,然后重新开放:**禁渔期间,存量从 27.6 一路恢复到 100;重新开放之后,努力量冲到 9.25,存量又跌到最低 21.8,和不禁渔时(9.26、21.7)几乎一样;到第 60 年以后,一切和不禁渔时没有区别:S = 40,租金 0.02。

问题 3 的答案:不会好。 禁渔让鱼回来了,可是没有改变"谁都能进"这条规则,所以鱼一多,船又涌进来,一切重来。这正是第 25 讲 3.3 节说的:悲剧的关键,是开放进入,不是别的。

要小心,不要过度解读:这里的"禁渔",是一个极端的简化(努力量定死为 0,十年,然后完全开放)。现实里的休渔制度(比如我国的海洋伏季休渔,和长江十年禁捕),是配合其他规则一起实行的(捕捞许可、渔具渔法限制,等等),不能从这个模型里,推断它们的效果。

六、第四步:社区自己管

第 25 讲的 Ostrom 一节,算过一个简单的监督门槛:偷捕的好处是 g,被发现的概率是 q,被罚停捕 k 期,损失的现值是 L(k),不偷捕的条件是 q × L(k) ≥ g。现在,让 10 个渔民,真的在渔场里,一年一年地做选择:

  • 约好每人出海 0.3(总努力量 3,正好最优)。每个渔民,每年可以守规矩(出海 0.3),或者偷捕(出海 0.6)。
  • 巡查每年以概率 q 发现一个偷捕的人,发现了,就让他停捕 k 年。
  • 渔民没有人教,他只会看自己最近 30 年的平均收入;每年有 10% 的概率,找一个邻居比一比,谁赚得多就学谁;另外有 1% 的概率,随便试一下另一种做法。
# 第四步:社区自己管(Ostrom)。10 个渔民,约好每人出海 0.3(总努力量 3,正好是最优)。
# 每个渔民,每年可以「守规矩」(出海 0.3),或者「偷捕」(出海 0.6)。巡查每年以概率 q 发现一个偷捕的人,发现了就让他停捕 k 年。
# 渔民没有人告诉他该怎么办:他只会看自己最近 30 年的平均收入;每年有 10% 的概率,找一个邻居比一比,谁赚得多就学谁;另外有 1% 的概率随便试一下另一种做法。
def community(q, k, n=10, years=1500, seed=0, window=30, imitate=0.1, mutate=0.01):
    g = np.random.default_rng(seed)
    S = 70.0; cheat = g.random(n) < 0.5; excluded = np.zeros(n, int); hist = np.zeros((n, window)); out = []
    for t in range(years):
        e = np.where(cheat, 0.6, 0.3) * (excluded == 0)
        caught = (g.random(n) < q) & cheat & (excluded == 0)
        pay = PRICE * Q * e * S - C * e                                  # 每个人这一年的利润
        S = max(S + R * S * (1 - S / K) - Q * e.sum() * S, 1e-3)
        excluded = np.maximum(excluded - 1, 0); excluded[caught] = k       # 被发现的,停捕 k 年
        hist[:, t % window] = pay; avg = hist[:, :min(t + 1, window)].mean(axis=1)
        for i in range(n):
            if g.random() < imitate:
                j = g.integers(n)
                if avg[j] > avg[i]: cheat[i] = cheat[j]
        flip = g.random(n) < mutate; cheat = np.where(flip, ~cheat, cheat)
        out.append((S, cheat.mean(), pay.sum()))
    return np.array(out)

# 守规矩的好处和偷捕的好处(鱼的存量 70 时):
S0 = 70.0; honest = PRICE * Q * 0.3 * S0 - C * 0.3; extra = (PRICE * Q * 0.6 * S0 - C * 0.6) - honest
print(f"鱼的存量 70 时:守规矩每年赚 {honest:.2f},偷捕比守规矩,每年多赚 {extra:.2f}。")
print("按第 25 讲的公式(折现因子 0.9),不偷捕的条件是 q ≥ 偷捕的好处 ÷ 被罚损失的现值:")
print("  停捕 k 年:" + ",".join(f"k={k_}:q ≥ {extra / sum(honest * 0.9 ** j for j in range(1, k_ + 1)):.3f}" for k_ in (1, 3, 10)))

print("\n模拟(最后 1000 年的平均;4 次独立模拟再平均):偷捕的渔民占几成 / 鱼的存量 / 渔民每年的总租金")
print("   q     k = 1 年                 k = 3 年                 k = 10 年")
comm = {}
for q in (0.0, 0.05, 0.1, 0.2, 0.4):
    cells = []
    for k in (1, 3, 10):
        res = np.mean([community(q, k, seed=s)[500:].mean(axis=0) for s in range(4)], axis=0); comm[(q, k)] = res
        cells.append(f"{res[1]:4.0%} / {res[0]:4.0f} / {res[2]:5.2f}")
    print(f"  {q:4.2f}   " + "      ".join(cells))
print("(全部守规矩:偷捕 0%,鱼的存量 70,总租金 9;没人管:几乎全部偷捕,存量只剩 44 左右,租金 2 左右。)")

输出:

鱼的存量 70 时:守规矩每年赚 0.90,偷捕比守规矩,每年多赚 0.90。
按第 25 讲的公式(折现因子 0.9),不偷捕的条件是 q ≥ 偷捕的好处 ÷ 被罚损失的现值:
  停捕 k 年:k=1:q ≥ 1.111,k=3:q ≥ 0.410,k=10:q ≥ 0.171

模拟(最后 1000 年的平均;4 次独立模拟再平均):偷捕的渔民占几成 / 鱼的存量 / 渔民每年的总租金
   q     k = 1 年                 k = 3 年                 k = 10 年
  0.00    87% /   44 /  2.00       87% /   44 /  2.00       87% /   44 /  2.00
  0.05    86% /   47 /  3.41       82% /   52 /  5.34       70% /   62 /  7.91
  0.10    85% /   49 /  4.48       80% /   57 /  6.96       57% /   70 /  8.56
  0.20    84% /   53 /  5.96       74% /   64 /  8.37       38% /   74 /  8.50
  0.40    84% /   59 /  7.69       54% /   71 /  8.81       24% /   75 /  8.47
(全部守规矩:偷捕 0%,鱼的存量 70,总租金 9;没人管:几乎全部偷捕,存量只剩 44 左右,租金 2 左右。)

左:不同的巡查概率 q 和惩罚 k 下,偷捕的渔民占多少;右:鱼的存量

逐段看:

  • 没有人管(q = 0):87% 的渔民偷捕(剩下的 13% 是随便试的),鱼的存量只有 44,总租金 2.0。
  • 罚得太轻,再勤的巡查也管不住:k = 1 年,q 从 0 加到 0.4,偷捕的比例,几乎不动(87% → 84%),因为按公式,门槛是 q ≥ 1.11,不可能。
  • 罚得越重,需要的巡查越少:k = 3 年,q = 0.4 时,偷捕降到 54%(公式的门槛 0.41);k = 10 年,q = 0.1 时,偷捕降到 57%,q = 0.2 时降到 38%,鱼的存量 74,租金 8.5(公式的门槛 0.17)。
  • 模拟里的数字,在门槛附近,不是一下子降到零,而是明显下降:渔民是靠"看自己最近的收入"和"模仿邻居"在学,不是在解公式;学习有惯性,也有试错的噪声。

问题 4 的答案:要看罚多重。 停捕 1 年,管不住;停捕 3 年,查到每年 4 成,偷捕仍有 54%;停捕 10 年,每年只查 2 成,偷捕就降到 38%。这和第 25 讲的结论,是一致的:监督和惩罚要配合,罚得越重,需要的监督越少;可是巡查要花钱,重罚也有被冤枉的风险和接受度的问题。

七、第五步:Schelling 的城市

第二部分开始。先看一个最简单的设定:每个人要求"邻居里至少 30% 和我同类"。

# ============ 第二部分:Schelling 的隔离模型 ============
# 一个 L × L 的格子(边界首尾相连),格子里住着两种人(红、蓝,各占 45%),10% 的格子是空的。
# 每个人的"偏好"只有一条:周围 8 个邻居里,如果和自己同一种的人占的比例(只算有人住的邻居)不低于 tau,我就满意,否则我想搬走。
# 想搬走的人,搬到一个随机的空格子里,只要搬过去就满意(这样的空格子没有,就留在原地)。一次又一次,直到没有人想搬,或者 200 轮到了。
EMPTY, RED, BLUE = 0, 1, 2
NB = [(dx, dy) for dx in (-1, 0, 1) for dy in (-1, 0, 1) if (dx, dy) != (0, 0)]

def same_fraction(grid, x, y, kind):
    """如果一个 kind 种类的人住在 (x, y),他的邻居里,同类占已住人的比例;没有邻居返回 1.0(不会不满意)"""
    L = grid.shape[0]; same = occupied = 0
    for dx, dy in NB:
        v = grid[(x + dx) % L, (y + dy) % L]
        if v != EMPTY: occupied += 1; same += (v == kind)
    return 1.0 if occupied == 0 else same / occupied

def make_grid(L=40, empty=0.10, seed=0):
    g = np.random.default_rng(seed); cells = np.zeros(L * L, dtype=int)
    n_empty = int(empty * L * L); n_each = (L * L - n_empty) // 2
    cells[:n_each] = RED; cells[n_each:2 * n_each] = BLUE
    g.shuffle(cells); return cells.reshape(L, L), g

def similarity(grid):
    """所有人的"邻居里同类占的比例"的平均值(完全随机混居大约是 0.5;完全隔离是 1.0)"""
    L = grid.shape[0]; vals = [same_fraction(grid, x, y, grid[x, y]) for x in range(L) for y in range(L) if grid[x, y] != EMPTY]
    return float(np.mean(vals))

def dissimilarity_index(grid, block=8):
    """差异指数(Duncan 指数):把格子切成 block × block 的小块("街区"),D = 1/2 × Σ |该街区红人比例 - 该街区蓝人比例|,0 = 完全混居,1 = 完全隔离"""
    L = grid.shape[0]; r = np.array([[np.sum(grid[i:i + block, j:j + block] == RED) for j in range(0, L, block)] for i in range(0, L, block)], float)
    b = np.array([[np.sum(grid[i:i + block, j:j + block] == BLUE) for j in range(0, L, block)] for i in range(0, L, block)], float)
    return float(0.5 * np.abs(r / r.sum() - b / b.sum()).sum())

def unhappy_share(grid, taus, hi=1.0):
    L = grid.shape[0]; cells = [(x, y) for x in range(L) for y in range(L) if grid[x, y] != EMPTY]
    return float(np.mean([not (taus[x, y] <= same_fraction(grid, x, y, grid[x, y]) <= hi) for x, y in cells]))

def run_schelling(tau, seed=0, L=40, max_rounds=200, tau_of=None, hi=1.0, empty=0.10):
    """tau_of:可选,给每个人单独的 tau(函数,返回和 grid 同形状的数组;人搬家时,他的 tau 跟着走),默认大家一样。
    hi:可选,邻居里同类的比例,如果高于 hi,也会不满意(「不喜欢太单一」),默认 1.0,即没有这个要求"""
    grid, g = make_grid(L, empty=empty, seed=seed)
    taus = np.full(grid.shape, tau, float) if tau_of is None else tau_of(grid, g)
    history = [(0, similarity(grid), 0)]
    for rnd in range(1, max_rounds + 1):
        people = [(x, y) for x in range(L) for y in range(L) if grid[x, y] != EMPTY]
        g.shuffle(people); moved = 0
        for (x, y) in people:
            kind = grid[x, y]
            if taus[x, y] <= same_fraction(grid, x, y, kind) <= hi: continue          # 满意,不动
            empties = np.argwhere(grid == EMPTY); g.shuffle(empties)
            for (ex, ey) in empties:                                                # 找一个随机的、搬过去会满意的空格子
                grid[x, y] = EMPTY                                                  # 先把自己拿走,再看新位置的邻居
                if taus[x, y] <= same_fraction(grid, ex, ey, kind) <= hi:
                    grid[ex, ey] = kind; taus[ex, ey] = taus[x, y]; moved += 1; break
                grid[x, y] = kind
        history.append((rnd, similarity(grid), moved))
        if moved == 0: break
    return grid, taus, history

grid0, _ = make_grid(seed=0)
print(f"随机混居的开始:邻居里同类的平均比例 = {similarity(grid0):.3f},差异指数 D = {dissimilarity_index(grid0):.3f}")
print("\n每个人的要求 tau = 0.30(只要求邻居里至少 30% 和自己同类,也就是能接受自己是 70% 对 30% 的少数):")
grid, taus, hist = run_schelling(0.30, seed=0)
for rnd in (0, 1, 2, 5, 10, len(hist) - 1):
    if rnd < len(hist): print(f"  第 {hist[rnd][0]:>2} 轮:邻居里同类的平均比例 {hist[rnd][1]:.3f};这一轮搬家的人数 {hist[rnd][2]}")
print(f"最后:差异指数 D = {dissimilarity_index(grid):.3f};共 {len(hist) - 1} 轮后,没有人想搬。")

输出:

随机混居的开始:邻居里同类的平均比例 = 0.499,差异指数 D = 0.101

每个人的要求 tau = 0.30(只要求邻居里至少 30% 和自己同类,也就是能接受自己是 70% 对 30% 的少数):
  第  0 轮:邻居里同类的平均比例 0.499;这一轮搬家的人数 0
  第  1 轮:邻居里同类的平均比例 0.699;这一轮搬家的人数 270
  第  2 轮:邻居里同类的平均比例 0.742;这一轮搬家的人数 54
  第  5 轮:邻居里同类的平均比例 0.756;这一轮搬家的人数 0
  第  5 轮:邻居里同类的平均比例 0.756;这一轮搬家的人数 0
最后:差异指数 D = 0.279;共 5 轮后,没有人想搬。

逐段看:

  • 随机混居的开始:邻居里同类的平均比例 0.499(两种人各一半,随机混居,约 0.5);用"街区"(把格子切成 8 × 8 的小块)算的差异指数 D = 0.101(0 是完全混居,1 是完全隔离)。
  • 第 1 轮,270 个人想搬:搬完之后,邻居里同类的平均比例,从 0.499,一下子升到 0.699。第 2 轮 54 个人搬家,第 5 轮没有人想搬了:最后 0.756,D = 0.279。

每个人只要求"至少 30% 和我同类",也就是说,每个人都能接受自己是 70% 对 30% 的少数;最后,一个人的邻居里,平均有 75.6% 和自己同类。这是第五步想让你看到的。

八、第六步:要求越高,隔离越重,再高就搬不动了

对不同的 tau,各跑 8 个不同的随机开头,取平均:

# 要求 tau 变化时,结果怎么变?每个 tau,用 8 个不同的随机开头,取平均(每次最多 200 轮)。
print("\n每个人的要求 tau 不同:8 个随机开头的平均")
print("   tau   最后邻居里同类的比例   差异指数 D   最后还不满意的人的比例   平均多少轮停下来")
sweep, snaps = {}, {}
for tau in (0.10, 0.20, 0.30, 0.375, 0.45, 0.50, 0.55, 0.625, 0.70, 0.80):
    sims, ds, rounds, unh = [], [], [], []
    for seed in range(8):
        grid, taus, hist = run_schelling(tau, seed=seed, max_rounds=120)
        if seed == 0: snaps[tau] = grid.copy()
        sims.append(hist[-1][1]); ds.append(dissimilarity_index(grid)); rounds.append(len(hist) - 1); unh.append(unhappy_share(grid, taus))
    sweep[tau] = (np.mean(sims), np.mean(ds), np.mean(unh), np.mean(rounds))
    print(f"  {tau:5.3f}   {sweep[tau][0]:14.3f}         {sweep[tau][1]:8.3f}          {sweep[tau][2]:10.1%}               {sweep[tau][3]:8.1f}")

输出:


每个人的要求 tau 不同:8 个随机开头的平均
   tau   最后邻居里同类的比例   差异指数 D   最后还不满意的人的比例   平均多少轮停下来
  0.100            0.515            0.104                0.0%                    2.0
  0.200            0.573            0.134                0.0%                    3.8
  0.300            0.748            0.247                0.0%                    6.0
  0.375            0.765            0.251                0.0%                    5.9
  0.450            0.864            0.352                0.0%                    7.4
  0.500            0.864            0.352                0.0%                    7.4
  0.550            0.921            0.466                0.0%                    9.2
  0.625            0.958            0.558                0.7%                    9.0
  0.700            0.544            0.130               69.7%                    2.0
  0.800            0.515            0.107               91.0%                    2.0

开始时随机混居的格子,和 tau = 0.3、0.5、0.625 时,最后的格子(红、蓝是两种人,浅灰是空格)

左:隔离程度随 tau 的变化;右:只有一部分人有要求时(第七步)

逐段看:

  • tau = 0.1、0.2(要求很低):几乎没有隔离(0.515、0.573)。
  • tau = 0.3:0.748。要求只比"随机混居"(0.5)高出一点点,结果的隔离却已经很明显。
  • tau = 0.45、0.5:0.864,D = 0.352;tau = 0.55:0.921;tau = 0.625:0.958,D = 0.558:两个群体几乎各占一片(看图的最右)。
  • tau = 0.7、0.8:突然又变成 0.544、0.515,而且 69.7%、91.0% 的人,最后还不满意。这不是和谐:是僵局。要求太高,几乎没有"搬过去会满意"的空格子,大家不满意地,留在原地。(按我们的规则,找不到会满意的格子,就留在原地。)
  • tau = 0.45 和 0.5,结果一模一样:不是 bug。一个人最多 8 个邻居,同类比例只能是 k 分之几的分数(k ≤ 8),在 0.45 到 0.5 之间,没有这样的分数。练习 5 让你自己想。

一个结论:个体的要求,温和到不能再温和,群体的结果,却可以非常隔离。 原因是:每个人搬走,都让原来的邻居,同类比例变化(少了一个同类,或者多了一个异类),让别人也更可能不满意,这是一个连锁。每个人的搬家,对邻居是外部性(第 23 讲)。

九、第七步:变一变

两个变化,看看结论有多稳。每种设定,8 个随机开头的平均。

# 变一变:(一)只有一部分人有要求;(二)人们不喜欢太单一的邻居。每种设定,用 8 个随机开头的平均。
def mixed_tau(share, tau_high, tau_low=0.0):
    """share 比例的人,要求 tau_high;其余的人,要求 tau_low(0 = 完全无所谓)"""
    def f(grid, g):
        picks = g.random(grid.shape) < share
        return np.where(picks, tau_high, tau_low).astype(float)
    return f

def avg_over_seeds(**kw):
    res = []
    for seed in range(8):
        grid, taus, hist = run_schelling(seed=seed, max_rounds=120, **kw)
        res.append((hist[-1][1], dissimilarity_index(grid), unhappy_share(grid, taus, kw.get("hi", 1.0))))
    return np.mean(res, axis=0)

print("\n(一)只有一部分人有要求(要求 tau = 0.5),其余的人,对邻居完全无所谓(tau = 0):")
print("  有要求的人占   最后邻居里同类的比例   差异指数 D   最后还不满意的人的比例")
mix = {}
for share in (0.0, 0.1, 0.25, 0.5, 1.0):
    mix[share] = avg_over_seeds(tau=0.0, tau_of=mixed_tau(share, 0.5))
    print(f"      {share:4.0%}          {mix[share][0]:9.3f}              {mix[share][1]:8.3f}           {mix[share][2]:8.1%}")

print("\n(二)每个人要求「同类至少 tau」,同时「同类也不要超过 hi」(不喜欢太单一的邻居):")
print("  tau    hi    最后邻居里同类的比例   差异指数 D   最后还不满意的人的比例")
two = {}
for tau, hi in ((0.3, 1.0), (0.3, 0.8), (0.3, 0.7), (0.3, 0.6), (0.5, 0.75)):
    two[(tau, hi)] = avg_over_seeds(tau=tau, hi=hi)
    print(f"  {tau:.2f}  {hi:.2f}         {two[(tau, hi)][0]:9.3f}              {two[(tau, hi)][1]:8.3f}           {two[(tau, hi)][2]:8.1%}")

输出:


(一)只有一部分人有要求(要求 tau = 0.5),其余的人,对邻居完全无所谓(tau = 0):
  有要求的人占   最后邻居里同类的比例   差异指数 D   最后还不满意的人的比例
        0%              0.503                 0.099               0.0%
       10%              0.533                 0.114               0.0%
       25%              0.578                 0.142               0.0%
       50%              0.665                 0.201               0.0%
      100%              0.866                 0.392               0.0%

(二)每个人要求「同类至少 tau」,同时「同类也不要超过 hi」(不喜欢太单一的邻居):
  tau    hi    最后邻居里同类的比例   差异指数 D   最后还不满意的人的比例
  0.30  1.00             0.748                 0.247               0.0%
  0.30  0.80             0.581                 0.142              19.0%
  0.30  0.70             0.508                 0.107              27.3%
  0.30  0.60             0.476                 0.082              32.3%
  0.50  0.75             0.615                 0.156              37.2%

逐段看:

  • (一)只有一部分人有要求(要求 tau = 0.5),其余的人对邻居完全无所谓:有要求的人占 0%,邻居里同类 0.503;10%:0.533;25%:0.578;50%:0.665;100%:0.866。少数有要求的人,不会一个人撬动全部:隔离随有要求的人的比例,逐渐加重。但注意,即使只有 10%,也比纯随机多了一点(0.503 → 0.533),而 100% 的时候,是 0.866。
  • (二)人们也不喜欢太单一的邻居:每个人要求"同类至少 tau,同时同类不超过 hi"。tau = 0.3、hi = 1(没有上限):0.748;hi = 0.8:0.581;hi = 0.7:0.508(几乎和随机混居一样);hi = 0.6:0.476。 也就是说:如果每个人,不只是怕"自己是少数",也不喜欢"太单一",隔离就几乎消失了。 但是,要注意最后一列:在 hi = 0.8、0.7、0.6 时,19.0%、27.3%、32.3% 的人最后还是不满意:不是所有人都找到了"刚刚好"的邻居,只是大家都搬不动。

问题 5 的答案:75.6%,远高于任何人要求的 30%。 问题 6 的答案:不会轻很多,但会轻一些,而且是逐渐的。

这个模型能说明现实里的隔离吗

这是一个有争议的话题,按"理论预测 → 证据与局限 → 分歧在哪 → 价值判断留给读者"来写。

理论预测什么:很温和的偏好,加上"搬走的人会改变邻居的构成",就足以产生明显的隔离;而且有"临界点":邻居里少数群体的比例过了某个点,原来的居民会陆续搬走,变化会自己加速。Schelling 1971 年的文章里,除了这里用的模拟,还有一个"邻里翻转"(tipping)的分析模型。

证据怎么说:Card、Mas 和 Rothstein 2008 年(Quarterly Journal of Economics,123 卷 1 期,177–218 页)用美国 1970 到 2000 年的普查区数据,发现白人居民的迁出,在大多数城市里,确实表现出翻转的特征:当一个社区里少数族裔的比例,超过某个临界值(据他们的估计,不同城市在 5% 到 20% 之间),白人居民的迁入,会明显变少。他们还发现,房价和租金,在临界点附近,没有明显的非线性;而且,白人居民对种族的态度越宽容的城市,临界点越高。(我只核对了摘要。)

局限:我们的模型,把"偏好"当成隔离的唯一原因。现实里,收入差异、歧视(比如贷款、租房、销售中的差别对待)、住房政策、学区、信息和社交网络,都会影响一个人住在哪里。模型能推出隔离,不能证明现实里的隔离,就是这样来的。

分歧在哪:事实上:现实里的隔离,有多少来自居民的偏好,有多少来自歧视和制度的限制?这是一个实证问题,答案因地方、因时代而不同。价值上:见下。

价值判断(留给你):

  1. 一个社会,应不应该把"居民的选择自由"看得比"社区的混居程度"更重要?
  2. 如果温和的偏好,也可能导致强烈的隔离,政府要不要干预?干预的话,是限制歧视,是补贴混居,还是别的?
  3. 对"隔离"本身,该看居住的分布,还是看机会(学校、工作、公共服务)的差别?

经济学可以帮你把每种办法的效果、代价和激励算清楚,不能替你选。

十、这个实验说明了什么,没说明什么

说明了:

  1. 公地悲剧不只是一个"稳定之后"的数字,还是一个过程:开放进入,加上时滞,会让资源一轮一轮地被过度开发,甚至跌到濒危。
  2. “看平均利润"和"算边际”,差别很大:一个只看平均利润的主人,也会把渔场耗尽。
  3. 只管资源,不管进入规则,白管:禁渔十年,鱼回来了,开放之后,一切重来。
  4. 监督要配合惩罚:罚得太轻,再勤的巡查也没用;罚得重,需要的巡查少得多。
  5. 个体的小动作,会累积成整体的大结果:温和的要求,也能产生强烈的隔离;而这个结果,不是任何一个人想要的。

没说明:

  1. 渔场的数字不是现实:logistic 增长、固定的价格和成本、同质的渔民,都是为了讲清楚机制。
  2. 渔民是怎么调整的,是我们假设的(看平均利润、按时滞调整、模仿邻居);真实渔民的行为,更复杂。
  3. Schelling 模型只说明"可能",没有说明"事实":见上面的证据和局限。
  4. 两部分,都是一个具体的设定;换一种搬家规则、换一种邻居的定义,数字会变。

十一、和前面、后面的联系

  • 第 19 讲:重复互动、监督与惩罚,是社区管理成功的条件。
  • 第 23 讲:外部性。公地悲剧,和一个人搬家对邻居的影响,都是外部性。
  • 第 25 讲:这个实验的第一部分,是第 25 讲 3 节、4 节的模拟版;第 25 讲的 Costello 等人的证据,在第三步里被"冲过头"的机制补了一块。
  • 第 17 讲:临界规模和翻转,Schelling 的"翻转"是同一类结构。
  • 实验 73:格子上的合作;这里的格子,是人在选择"住哪里"。
  • 实验 77:Solow 增长模拟与真实数据校准。

十二、练习

下面的练习,都可以在上面的代码上改几个数字做出来。提示里的数字,都是我跑过的。

练习 1(成本变了)。 把 c 从 4 改成 6(直接改 C = 6.0,再重新算稳态)。租金最大的努力量、开放进入的努力量、渔获最大的努力量,各是多少?最大租金多少?

提示:租金最大 E = 2,最大租金 4.0;开放进入 E = 4;渔获最大仍是 E = 5(和成本无关,只取决于鱼自己怎么长)。成本越高,开放进入时出海的人越少,可是和最优的差,仍然是 2 单位。

练习 2(时滞更长)。 run_fishery(speed=0.5, lag=4):努力量最高冲到多少,鱼的存量最低多少?

提示:E 最高冲到 18.09,S 最低 0.119,最后 30 年,E 在 0 到 17.23 之间来回。时滞从 3 年变成 4 年,鱼的存量,从 1.6 变成 0.12:在真实的渔场里,这相当于绝迹。(我们这个模型,没有"鱼少到一定程度,就再也长不回来"的机制;真实的生态里可能有。)

练习 3(税率)。 对每单位努力征税 t = 1.5、3、5,长期的 E、S、税收各是多少?税率多少,税收最多?

提示:用 run_fishery(speed=0.5, lag=1, cost=C + t),取第 60 年以后的平均:t = 1.5:E = 4.5,S = 55,税收 6.75;t = 3:E = 3.0,S = 70,税收 9.0;t = 5:E = 1.0,S = 90,税收 5.0。税收 = t × (6 − t),在 t = 3 时最大,这个税率,也正好让总租金最大。(税率太高,税基萎缩,这是实验 71 里的拉弗曲线。)

练习 4(没有试错的社区)。 第四步里,把 mutate 改成 0(渔民不再随便试另一种做法)。q = 0、k = 3;q = 0.4、k = 3;q = 0.4、k = 10,偷捕的比例各是多少?

提示:community(q, k, mutate=0),4 次模拟,第 500 年以后的平均:q = 0、k = 3:偷捕 100%,鱼的存量 40,租金 0;q = 0.4、k = 3:偷捕 50%,存量 70.9,租金 8.8;q = 0.4、k = 10:偷捕 0%,存量 70,租金 9.0。没有噪声,要么全部偷,要么完全不偷,取决于罚得够不够重:k = 10、q = 0.4,社区回到完全合作。

练习 5(为什么一样)。 第六步里,tau = 0.45 和 tau = 0.5,所有的结果都一模一样。为什么?

提示:一个人最多 8 个邻居,已住人的邻居数 k ≤ 8,同类的比例是 m ÷ k。在 [0.45, 0.5) 里,没有这样的分数:3/7 = 0.43 太小,4/8 = 0.5 刚好到。所以"≥ 0.45"和"≥ 0.5",对每一个人,都是同一个判断。同样,tau = 0.3 和 0.375,不是同一个门槛(3/8 = 0.375),它们的结果也不同(0.748 和 0.765)。

练习 6(空格多少)。 第五步里,把空格的比例(run_schelling 的 empty)从 0.10 改成 0.05、0.20、0.30,tau = 0.5,邻居里同类的平均比例、差异指数、停下来的轮数,有什么变化?

提示:4 个随机开头的平均:空格 5%:同类比例 0.862,D 0.373,7.0 轮;10%:0.862,0.381,8.0 轮;20%:0.874,0.405,6.75 轮;30%:0.869,0.364,6.25 轮。空格的多少,几乎不影响最后的隔离程度,只轻微地影响停下来要多久。隔离的原因,是"要求",不是"有没有空地"。

十三、局限

  • 渔场:logistic 增长,没有随机波动,没有"鱼少到一定程度就长不回来";价格和成本固定;渔民同质。
  • 调整规则:努力量按"利润"和"时滞"调整,社区里的渔民按"模仿"调整,都是我们假设的。
  • 社区治理只模拟了一种最简单的巡查和惩罚(发现一次,停捕 k 年)。没有讨论监督的成本、分级惩罚、规则怎么来(Ostrom 的其他特点)。
  • Schelling:格子是方的,邻居是 8 个,边界首尾相连,搬家的规则是"搬到随机的、会满意的空格"。换别的规则,结果会变。
  • 没有:收入、价格(房租)、歧视、制度、人的搬家成本。

参考

  • Schaefer, M. B. (1954). Some aspects of the dynamics of populations important to the management of the commercial marine fisheries. Bulletin of the Inter-American Tropical Tuna Commission, 1(2), 27–56.
  • Gordon, H. S. (1954). The economic theory of a common-property resource: The fishery. Journal of Political Economy, 62(2), 124–142.
  • Hardin, G. (1968). The tragedy of the commons. Science, 162(3859), 1243–1248.
  • Ostrom, E. (1990). Governing the Commons. Cambridge University Press.
  • Schelling, T. C. (1971). Dynamic models of segregation. Journal of Mathematical Sociology, 1(2), 143–186.
  • Schelling, T. C. (1978). Micromotives and Macrobehavior. W. W. Norton.
  • Duncan, O. D., & Duncan, B. (1955). A methodological analysis of segregation indexes. American Sociological Review, 20(2), 210–217.
  • Card, D., Mas, A., & Rothstein, J. (2008). Tipping and the dynamics of segregation. Quarterly Journal of Economics, 123(1), 177–218.