天赐范式第198天·第一篇:让策略开始起源 ------ 突变翻转类型下的自发起源
摘要
全A起步,突变以概率μ翻转类型,三臂×20seed。B自发起源:min首突体优势1.96×、sqrt 7.0×,到均衡中位数7/6代。外置各钉角色最优,内置β_A塌向0.5角色消解。④前件③齐备。

1 回扣:④的前件 2/3,本篇补第三件
197-1《让稀有开始占优》走通频率依赖选择:同一环境多策略可维持(前件①可维持)。
197-2《让奖励开始外置》走通奖励外置:A/B 两型互补共存,25/25 自稳定(前件②可互补)。
但两篇的策略是预设的------初始就放好 A/B 两型各 25,不是从突变起源。④自发分工还欠前件③「可起源」:策略能不能从突变中自发起源?
本篇初始全 A 型,突变以概率 μ 翻转类型(A↔B),看 B 能否自发起源、建立、到均衡。
2 实验设计
复刻 197-2 骨架(禁改既有机制):
- N = 50;型间确定性复制子更新(T_k = S_k + P,n_A' = round(N·T_A/(T_A+T_B)));型内 K=3 锦标赛 + 变异 σ_v = 0.02,β 截断 0,1
- 初始全 A 型,β ~ N(0.3, 0.01) 截断,seed = 42 起手式与 197-2 一致
- 角色最优 β*_A = 0.3 / β*_B = 0.7;q_i = 1/(1+(β_i−β*_type)²)
突变(本篇唯一新机制):refill 产生每个后代时以概率 μ 翻转类型(A↔B),β 在新角色最优附近重采 N(β*_new, 0.01) 截断;不翻转则 β = 父代 + N(0, σ_v)。
三臂(同种子、同初值、同突变流,只变奖励/G):
| 臂 | 奖励 | 生产函数 |
|---|---|---|
| 外置 min | fitness = q_i + P/n_type | P = min(S_A, S_B) |
| 外置 sqrt | fitness = q_i + P/n_type | P = √(S_A·S_B) |
| 内置 | fitness = q(β, β_env) | β_env = 0.5(与 197-2 内置臂连续) |
μ ∈ {0.02, 0.05, 0.10},每臂每档 20 seed(seed 列表 range(42, 62),跨臂跨档同一组)。
3 首突体优势(49/1 实态解析)
全 A 起步,首突体出现时 49A/1B,q ≈ 1:
| 生产函数 | P | f_B | f_A | 优势比 |
|---|---|---|---|---|
| min | 1.0 | 2.00 | 1.0204 | 1.96× |
| sqrt | 7.0 | 8.00 | 1.1429 | 7.00× |
min 给 P = min(49, 1) = 1,稀有型 B 每个体分到 P/1 = 1.0;sqrt 给 P = √(49×1) = 7,每个 B 分到 7.0。弱互补(sqrt)的稀有型优势大得多------首突体入侵更快。
4 μ=0.05 seed=42:三臂起源轨迹
组成 n_A(t):
| 代 | 外置 min | 外置 sqrt | 内置 |
|---|---|---|---|
| 0 | 50 | 50 | 50 |
| 2 | 44 | 41 | 46 |
| 4 | 35 | 29 | 49 |
| 6 | 27 | 26 | 48 |
| 8 | 27 | 27 | 46 |
| 10 | 24 | 26 | 49 |
| 15 | 24 | 24 | 49 |
| 20 | 23 | 23 | 48 |
| 50 | 25 | 25 | 47 |
| 100 | 23 | 26 | 46 |
外置 min 约 10 代到 25±1,外置 sqrt 约 6 代------sqrt 首突体优势 7.0× vs min 1.96×,入侵更快,与 §3 解析一致。内置臂 n_A 长期偏高(46--49),B 零星出现但不建立------角色消解(§7 详述)。
β 均值终态:
| 臂 | β_A | β_B |
|---|---|---|
| 外置 min | 0.3021 | 0.6984 |
| 外置 sqrt | 0.2928 | 0.7081 |
| 内置 | 0.5034 | 0.7023 |
外置两臂 β 各钉角色最优(0.3/0.7),内置 β_A 塌向 0.5------角色消解。
5 μ=0.05 20 seed:起源时间分布
| 臂 | 首起源(n_B≥5) 中位数 | 极值 | 到均衡(|n_A−25|≤1) 中位数 | 极值 |
|---|---|---|---|---|
| 外置 min | 2 | 1~3 | 7 | 5~11 |
| 外置 sqrt | 2 | 1~2 | 6 | 4~10 |
两臂首起源都在第 2 代(突变率 μ=0.05,50 个体每代期望 2.5 个翻转,B 达 5 个约需 2 代)。到均衡 sqrt 比 min 快 1 代(6 vs 7),与首突体优势差一致。
6 μ 三档 churn:组成振荡极差
到均衡后组成在 25 附近振荡(μ=0.05 极差约 8,μ=0.10 极差约 10,见下表)------突变在均衡附近空转,churn 是本篇读数不是噪声。
| μ | 外置 min 极差 | 外置 sqrt 极差 |
|---|---|---|
| 0.02 | 5.7 | 5.8 |
| 0.05 | 8.6 | 8.0 |
| 0.10 | 10.6 | 10.5 |
μ 越大 churn 越大。μ=0.10 时组成极差约 10.6------突变每代期望 5 个翻转,组成在 20--30 间大幅摆动但仍不塌缩。
7 内置臂角色消解(20 seed)
内置单环境 β_env = 0.5,全体 β 塌到唯一环境最优:
| μ | β_A 终态 | β_B 条件均值 | n_B=0 seed 数 | n_A 标签 |
|---|---|---|---|---|
| 0.02 | 0.4995 | 0.7077 | 9/20 | 49.2 |
| 0.05 | 0.4993 | 0.7022 | 1/20 | 47.8 |
| 0.10 | 0.4990 | 0.6998 | 0/20 | 45.5 |
β_A 终态 ≈ 0.499------塌向 β_env = 0.5(选择把多数派 β 拉向环境最优)。β_B 条件均值三档恒定 ≈0.70,与 μ 无关:B 的 fits 系统性低于 A(B/A≈0.96),全局锦标赛里短命,存活 B 几乎全是当代新突变体(β 重置值 0.7)。μ 只改变 B 的平衡密度(n_A 标签 49.2→45.5)。角色结构半消解:A 角色被环境吞没,B 角色靠突变注入维持。型标签近中性偏弱劣势,组成由突变-选择平衡决定,无恢复力。
对照外置臂:β_A 钉 0.3、β_B 钉 0.7(§4),两角色都靠选择维持。本篇论点:外置分成让角色结构靠选择维持,内置让 A 角色消解------不是清除,是消解。
8 ④自发分工前件状态
| 前件 | 篇目 | 机制 | 状态 |
|---|---|---|---|
| ①可维持 | 197-1 | 频率依赖选择 | 可复现 |
| ②可互补 | 197-2 | 奖励外置 | 可复现 |
| ③可起源 | 本篇 | 突变翻转类型 | 可复现 |
| ④合流 | --- | 三件同一运行 | Future Work |
三前件各有可复现 demo,但尚未在同一运行中合流(合流列 Future Work)。「路径」一词仍专指 172-1 能力五步,篇目分组称「篇目分期」。
Limitations & Future Work
Limitations:
- 突变得的是类型,互补结构(生产函数 G)仍是预设的。
- 外置臂型间确定性更新下 B 一旦出现即建立(无建立失败);内置臂 B 不建立(低密度)。若型间改用抽样,小概率建立失败(197-2 同源),多 seed 已部分覆盖。
- 三前件各有 demo,尚未在同一运行中合流。
- 教学级,非生产安全。
Future Work:
- 三前件在同一运行中合流(199 方向)。
- 型间也用抽样更新 + 多 seed + 更大 N,看建立失败率。
Reproducibility
兑现 197-2 Future Work:多 seed + 策略自发起源。
三臂同种子同初值同突变流,只变奖励/G------隔离「奖励结构」为唯一变量。Python 3 标准库无第三方依赖,20 seed 固定列表,直接运行可复现全部结果。
附录:完整代码
python
# -*- coding: utf-8 -*-
"""
天赐范式 第198天 第一篇
让策略开始起源 ------ 自发起源
PID: TC-198A-V3.3.36.0
V3.3.36.0 · 2026-10-17
兑现 197-2 Future Work:多 seed + 策略自发起源。
初始全 A 型,突变以概率 μ 翻转类型(A↔B),β 在新角色最优附近重采。
三臂对照:外置 min / 外置 sqrt / 内置 β_env=0.5。
μ 三档 {0.02, 0.05, 0.10} × 20 seed。
"""
import sys
import math
import random
import unicodedata
sys.stdout.reconfigure(encoding="utf-8")
N_POP = 50
N_GENS = 100
TOURNAMENT_K = 3
SIGMA_V = 0.02
BETA_A = 0.3
BETA_B = 0.7
BETA_ENV = 0.5
SEEDS = list(range(42, 62))
MU_LIST = [0.02, 0.05, 0.10]
def bar(s=""):
print("=" * 74)
if s:
print(" " + s)
print("=" * 74)
def disp_w(s):
# 终端显示宽度:CJK 终端把 F/W 与 A(歧义) 字符都渲染成 2 列
return sum(2 if unicodedata.east_asian_width(ch) in ("F", "W", "A") else 1 for ch in s)
def md_table(headers, rows, aligns):
"""打印终端等宽的 markdown 表:列宽按显示宽度自适应,aligns='l'/'r'。"""
widths = [max(disp_w(str(c)) for c in col) for col in zip(headers, *rows)]
def fmt(row):
cells = []
for cell, w, a in zip(row, widths, aligns):
cell = str(cell)
gap = " " * (w - disp_w(cell))
cells.append(gap + cell if a == "r" else cell + gap)
return " | " + " | ".join(cells) + " |"
print(fmt(headers))
print(" |" + "|".join("-" * max(3, w + 2) for w in widths) + "|")
for row in rows:
print(fmt(row))
def q_i(beta, beta_star):
return 1.0 / (1.0 + (beta - beta_star) ** 2)
def clamp(x):
return max(0.0, min(1.0, x))
def init_pop_all_a(seed):
random.seed(seed)
betas = [clamp(BETA_A + random.gauss(0, 0.01)) for _ in range(N_POP)]
types = ['A'] * N_POP
return betas, types
def run_external(mu, seed, prod_fn):
betas, types = init_pop_all_a(seed)
history = []
for gen in range(N_GENS + 1):
q_vals = [q_i(betas[i], BETA_A if types[i] == 'A' else BETA_B) for i in range(N_POP)]
s_a = sum(q_vals[i] for i in range(N_POP) if types[i] == 'A')
s_b = sum(q_vals[i] for i in range(N_POP) if types[i] == 'B')
p = prod_fn(s_a, s_b)
n_a = sum(1 for t in types if t == 'A')
n_b = N_POP - n_a
mean_a = sum(betas[i] for i in range(N_POP) if types[i] == 'A') / n_a if n_a > 0 else 0
mean_b = sum(betas[i] for i in range(N_POP) if types[i] == 'B') / n_b if n_b > 0 else 0
history.append({
"gen": gen, "n_a": n_a, "mean_a": mean_a, "mean_b": mean_b,
"s_a": s_a, "s_b": s_b, "p": p,
})
if gen == N_GENS:
break
t_a = s_a + p
t_b = s_b + p
n_a_new = round(N_POP * t_a / (t_a + t_b)) if (t_a + t_b) > 0 else n_a
n_a_new = max(1, min(N_POP - 1, n_a_new))
a_indices = [i for i in range(N_POP) if types[i] == 'A']
b_indices = [i for i in range(N_POP) if types[i] == 'B']
new_betas = []
new_types = []
for _ in range(n_a_new):
pool = a_indices if len(a_indices) > 0 else b_indices
k = min(TOURNAMENT_K, len(pool))
candidates = random.sample(pool, k)
winner = max(candidates, key=lambda idx: q_vals[idx])
if random.random() < mu:
new_types.append('B')
new_betas.append(clamp(BETA_B + random.gauss(0, 0.01)))
else:
new_types.append('A')
new_betas.append(clamp(betas[winner] + random.gauss(0, SIGMA_V)))
for _ in range(N_POP - n_a_new):
pool = b_indices if len(b_indices) > 0 else a_indices
k = min(TOURNAMENT_K, len(pool))
candidates = random.sample(pool, k)
winner = max(candidates, key=lambda idx: q_vals[idx])
if random.random() < mu:
new_types.append('A')
new_betas.append(clamp(BETA_A + random.gauss(0, 0.01)))
else:
new_types.append('B')
new_betas.append(clamp(betas[winner] + random.gauss(0, SIGMA_V)))
betas = new_betas
types = new_types
return history
def run_builtin(mu, seed):
betas, types = init_pop_all_a(seed)
history = []
for gen in range(N_GENS + 1):
fits = [1.0 / (1.0 + (b - BETA_ENV) ** 2) for b in betas]
n_a = sum(1 for t in types if t == 'A')
n_b = N_POP - n_a
mean_a = sum(betas[i] for i in range(N_POP) if types[i] == 'A') / n_a if n_a > 0 else 0
mean_b = sum(betas[i] for i in range(N_POP) if types[i] == 'B') / n_b if n_b > 0 else 0
q_vals = [q_i(betas[i], BETA_A if types[i] == 'A' else BETA_B) for i in range(N_POP)]
s_a = sum(q_vals[i] for i in range(N_POP) if types[i] == 'A')
s_b = sum(q_vals[i] for i in range(N_POP) if types[i] == 'B')
history.append({
"gen": gen, "n_a": n_a, "mean_a": mean_a, "mean_b": mean_b,
"s_a": s_a, "s_b": s_b, "p": 0.0,
})
if gen == N_GENS:
break
new_betas = []
new_types = []
for _ in range(N_POP):
candidates = random.sample(range(N_POP), TOURNAMENT_K)
winner = max(candidates, key=lambda idx: fits[idx])
if random.random() < mu:
new_type = 'B' if types[winner] == 'A' else 'A'
new_beta_star = BETA_B if new_type == 'B' else BETA_A
new_types.append(new_type)
new_betas.append(clamp(new_beta_star + random.gauss(0, 0.01)))
else:
new_types.append(types[winner])
new_betas.append(clamp(betas[winner] + random.gauss(0, SIGMA_V)))
betas = new_betas
types = new_types
return history
def median(vals):
s = sorted(vals)
n = len(s)
return s[n // 2] if n % 2 == 1 else (s[n // 2 - 1] + s[n // 2]) / 2
def main():
bar("天赐范式 第198天 第一篇 让策略开始起源")
# ===== Part 0 回扣 =====
bar("Part 0 回扣:④的前件 2/3,本篇补第三件")
print(" 197-1 频率依赖:同环境多策略可维持(前件①)。")
print(" 197-2 奖励外置:互补共存可复现(前件②)。")
print(" 但策略是预设的,不是从突变起源------前件③「可起源」还欠。")
print(" 本篇:初始全 A 型,突变翻转类型,看 B 能否自发起源。")
print()
# ===== Part 1 首突体优势(解析) =====
bar("Part 1 首突体优势(49/1 实态解析)")
print(" 全 A 起步,首突体出现时 49A/1B,q≈1:")
s_a_49 = 49.0
s_b_1 = 1.0
p_min = min(s_a_49, s_b_1)
p_sqrt = math.sqrt(s_a_49 * s_b_1)
fit_a_min = 1.0 + p_min / 49
fit_b_min = 1.0 + p_min / 1
fit_a_sqrt = 1.0 + p_sqrt / 49
fit_b_sqrt = 1.0 + p_sqrt / 1
ratio_min = fit_b_min / fit_a_min
ratio_sqrt = fit_b_sqrt / fit_a_sqrt
print(f" min: P={p_min:.1f}, f_B/f_A = {fit_b_min:.2f}/{fit_a_min:.4f} ≈ {ratio_min:.2f}")
print(f" sqrt: P={p_sqrt:.1f}, f_B/f_A = {fit_b_sqrt:.2f}/{fit_a_sqrt:.4f} ≈ {ratio_sqrt:.2f}")
print(f" → sqrt 首突体优势 {ratio_sqrt:.1f}× vs min {ratio_min:.2f}×,入侵更快")
print()
# ===== Part 2 μ=0.05 seed=42 轨迹 =====
bar("Part 2 μ=0.05 seed=42:三臂起源轨迹")
mu0 = 0.05
hist_min = run_external(mu0, 42, lambda sa, sb: min(sa, sb))
hist_sqrt = run_external(mu0, 42, lambda sa, sb: math.sqrt(sa * sb))
hist_builtin = run_builtin(mu0, 42)
print(" 组成 n_A(t):")
rows = [[str(gen),
str(hist_min[gen]['n_a']),
str(hist_sqrt[gen]['n_a']),
str(hist_builtin[gen]['n_a'])] for gen in (0, 2, 4, 6, 8, 10, 15, 20, 50, 100)]
md_table(["代", "外置min", "外置sqrt", "内置"], rows, "rrrr")
print()
print(" β 均值终态:")
print(f" 外置min: β_A={hist_min[-1]['mean_a']:.4f}, β_B={hist_min[-1]['mean_b']:.4f}")
print(f" 外置sqrt: β_A={hist_sqrt[-1]['mean_a']:.4f}, β_B={hist_sqrt[-1]['mean_b']:.4f}")
print(f" 内置: β_A={hist_builtin[-1]['mean_a']:.4f}, β_B={hist_builtin[-1]['mean_b']:.4f}")
print(" → 外置各钉角色最优(0.3/0.7),内置 β_A 塌向 0.5(角色消解),β_B 由突变重置维持")
print()
# ===== Part 3 20 seed 起源时间分布 =====
bar("Part 3 μ=0.05 20 seed:起源时间分布")
mu0 = 0.05
for arm_name, run_fn in [
("外置min", lambda s: run_external(mu0, s, lambda sa, sb: min(sa, sb))),
("外置sqrt", lambda s: run_external(mu0, s, lambda sa, sb: math.sqrt(sa * sb))),
]:
first_orig = []
to_eq = []
for sd in SEEDS:
h = run_fn(sd)
fo = next((h[g]['gen'] for g in range(len(h)) if N_POP - h[g]['n_a'] >= 5), 999)
te = next((h[g]['gen'] for g in range(len(h)) if abs(h[g]['n_a'] - 25) <= 1), 999)
first_orig.append(fo)
to_eq.append(te)
print(f" {arm_name}:")
print(f" 首起源(n_B≥5): 中位数={median(first_orig):.0f} 极值={min(first_orig)}~{max(first_orig)}")
print(f" 到均衡(|n_A-25|≤1): 中位数={median(to_eq):.0f} 极值={min(to_eq)}~{max(to_eq)}")
print()
# ===== Part 4 μ 三档 churn 对比 =====
bar("Part 4 μ 三档 churn:组成振荡极差(20 seed 均值)")
rows = []
for mu in MU_LIST:
amps_min = []
amps_sqrt = []
for sd in SEEDS:
h_min = run_external(mu, sd, lambda sa, sb: min(sa, sb))
h_sqrt = run_external(mu, sd, lambda sa, sb: math.sqrt(sa * sb))
tail_min = [h['n_a'] for h in h_min[50:]]
tail_sqrt = [h['n_a'] for h in h_sqrt[50:]]
amps_min.append(max(tail_min) - min(tail_min))
amps_sqrt.append(max(tail_sqrt) - min(tail_sqrt))
avg_min = sum(amps_min) / len(amps_min)
avg_sqrt = sum(amps_sqrt) / len(amps_sqrt)
rows.append([f"{mu:.2f}", f"{avg_min:.1f}", f"{avg_sqrt:.1f}"])
md_table(["μ", "外置min 极差", "外置sqrt 极差"], rows, "rrr")
print(" → μ 越大 churn 越大(突变在均衡附近空转)")
print()
# ===== Part 5 内置臂角色消解 =====
bar("Part 5 内置臂角色消解(20 seed)")
for mu in MU_LIST:
mean_a_end = []
cond_mean_b = []
n_a_end = []
n_b_zero = 0
for sd in SEEDS:
h = run_builtin(mu, sd)
mean_a_end.append(h[-1]['mean_a'])
n_b = N_POP - h[-1]['n_a']
if n_b == 0:
n_b_zero += 1
else:
cond_mean_b.append(h[-1]['mean_b'])
n_a_end.append(h[-1]['n_a'])
avg_cond_b = sum(cond_mean_b) / len(cond_mean_b) if cond_mean_b else 0
print(f" μ={mu:.2f}: β_A终态={sum(mean_a_end)/len(mean_a_end):.4f}, "
f"β_B条件均值={avg_cond_b:.4f}, "
f"n_B=0 seed数={n_b_zero}/20, "
f"n_A标签={sum(n_a_end)/len(n_a_end):.1f}")
print(" → 内置单环境:β_A 塌向 0.5(角色消解),β_B 条件均值恒 ≈0.70(B 弱劣势 fits B/A≈0.96,短命,存活 B 几乎全是当代新突变体);型标签近中性偏弱劣势")
print()
# ===== Part 6 三前件状态表 =====
bar("Part 6 ④自发分工前件状态")
rows = [
["①可维持", "197-1", "频率依赖选择", "可复现"],
["②可互补", "197-2", "奖励外置", "可复现"],
["③可起源", "本篇", "突变翻转类型", "可复现"],
["④合流", "---", "三件同一运行", "Future Work"],
]
md_table(["前件", "篇目", "机制", "状态"], rows, "llll")
print()
# ===== Limitations & Future Work =====
bar("Limitations & Future Work")
print(" Limitations:")
print(" 1. 突变得的是类型,互补结构(生产函数 G)仍是预设的。")
print(" 2. 外置臂型间确定性更新下 B 一旦出现即建立(无建立失败);")
print(" 内置臂 B 不建立(低密度)。若型间改用抽样,小概率建立失败(197-2 同源)。")
print(" 3. 「分工」二字不出口:三前件各有 demo,尚未在同一运行中合流。")
print(" 4. 教学级,非生产安全。")
print()
print(" Future Work:")
print(" 1. 三前件在同一运行中合流(199 方向)。")
print(" 2. 型间也用抽样更新 + 多 seed + 更大 N,看建立失败率。")
print()
# ===== Reproducibility =====
bar("Reproducibility")
print(" 兑现 197-2 Future Work:多 seed + 策略自发起源。")
print(" 三臂同种子同初值同突变流,只变奖励/G------隔离「奖励结构」为唯一变量。")
print(" Python 3 标准库无第三方依赖,20 seed 固定列表,直接运行可复现全部结果。")
print()
if __name__ == "__main__":
main()

系列在建:天赐范式第198天,两篇制第一篇。接 197-2 Future Work 钩子------策略自发起源。三前件①可维持②可互补③可起源各有 demo,但尚未合流。留第二篇《让分成开始可验》。