天赐范式第185天·第二篇:让漂变开始定量------扫N看选择主导边界
摘要
补184-2的N侧。有限种群漂变每代注入σ_g/√N噪声,选择每代推h²·S。扫N从20到1000(6个格点,50 seeds),Δ*均值≈0.005不随N变------选择响应是确定性的;Δ*std随N按1/√N缩放,从0.00478(N=20)缩到0.00070(N=1000)。信噪比从2.29升到18.09;理论边界N*≈3.1(SNR=1处),实际N=100时SNR=5.57,选择可靠主导。"选择何时主导"完整两面:遗传力侧(h²>0.5)+ 种群侧(N>N*)。

一、接续184-2:还差N侧
第184-2篇回答了"选择何时主导"的遗传力侧:h²>0.5时选择力超过环境噪声,主导方向(且是平滑过渡,不是相变)。
但"选择何时主导"还有第二侧------种群侧。有限种群有一个遗传力公式管不到的东西:漂变。每代繁殖抽样只有N个个体,等位基因频率会在抽样时随机漂移,注入σ_g/√N量级的噪声。种群越小,漂变噪声越大;种群足够大时,选择信号才能盖过它。
184-2答了一半,这篇把N侧补上:扫N,看漂变噪声和选择响应谁主导、边界在哪。
二、量纲与预测
两个力的量纲:
- 漂变噪声 ≈ σ_g / √N(每代注入的抽样噪声)
- 选择响应 ≈ h² · S(每代选择的确定性推动)
边界定义在信噪比=1处:
N* ≈ (σ_g / (h²·S))²
预测:N > N* 时选择主导(std小、Δ*确定);N < N* 时漂变主导(std大、Δ*被噪声淹没)。
本篇S≈0.022(N=200时),与185-1的S≈0.021在同一量级------两篇都用高斯加权选择,微小差异来自σ_g实测波动和有限种群噪声。
三、实验设计
模型与185-1完全一致(x = g + e,g' = TARGET + β(g_sel − TARGET) + v),选择机制高斯加权(与185-1一致),避免跨篇混机制。
固定:β=0.3, σ_e=0.02, σ_v=0.020, gen=500, seeds=50, x*=0.8, ω=0.1, TARGET=0.5。
扫描:N ∈ {20, 50, 100, 200, 500, 1000} 六个格点,每个格点50个seed取均值±std。
四、N扫描结果
| N | h² | Δ* | S | σ_g | 漂变噪声 | 选择响应 | 信噪比 |
|---|---|---|---|---|---|---|---|
| 20 | 0.4773 | 0.00592±0.00478 | 0.02105 | 0.0196 | 0.004379 | 0.010046 | 2.29 |
| 50 | 0.5066 | 0.00561±0.00358 | 0.02120 | 0.0205 | 0.002899 | 0.010738 | 3.70 |
| 100 | 0.5205 | 0.00494±0.00193 | 0.02242 | 0.0209 | 0.002095 | 0.011672 | 5.57 |
| 200 | 0.5128 | 0.00549±0.00151 | 0.02192 | 0.0206 | 0.001454 | 0.011242 | 7.73 |
| 500 | 0.5240 | 0.00516±0.00119 | 0.02331 | 0.0210 | 0.000939 | 0.012212 | 13.00 |
| 1000 | 0.5221 | 0.00522±0.00070 | 0.02291 | 0.0209 | 0.000661 | 0.011962 | 18.09 |
三个观察:
- Δ*均值≈0.005,不随N变化(0.00494--0.00592,无单调趋势)。选择响应是确定性力------群体大小不影响方向,只影响确定程度。这直接对上了185-1的闭式解:Δ*由景观参数和h²决定,N不改变Δ*的期望值。
- Δ*std随N缩小:0.00478(N=20)→ 0.00070(N=1000)。50个seed间的离散度就是漂变的指纹。
- 信噪比单调上升:SNR = 选择响应/漂变噪声 = (h²·S)/(σ_g/√N),从2.29(N=20)升到18.09(N=1000)。
五、Δ*离散度随N缩放
| N | Δ*均值 | Δ*std | std/均值 | 1/√N标度 |
|---|---|---|---|---|
| 20 | 0.00592 | 0.00478 | 0.807 | 0.2236 |
| 50 | 0.00561 | 0.00358 | 0.638 | 0.1414 |
| 100 | 0.00494 | 0.00193 | 0.390 | 0.1000 |
| 200 | 0.00549 | 0.00151 | 0.275 | 0.0707 |
| 500 | 0.00516 | 0.00119 | 0.230 | 0.0447 |
| 1000 | 0.00522 | 0.00070 | 0.134 | 0.0316 |
std按1/√N缩放成立。 看std/均值:0.807 → 0.134,与1/√N标度(0.2236 → 0.0316)同比例收缩------两者比值在3.6--5.1倍区间波动,没有随N系统发散。这意味着Δ*的seed间离散度确实是漂变主导的1/√N行为,不是别的噪声源。
N=100后std/均值已压到0.39以下,每个seed的Δ*都在均值约40%以内;N=1000时降到0.134,选择结果高度确定。
六、选择主导边界N*
用大N参考值估算:
- σ_g = 0.0209
- h² = 0.5221
- S = 0.02291
- h²·S = 0.011962
- N* = (σ_g/(h²·S))² = 3.1
信噪比分布:
| N | SNR |
|---|---|
| 20 | 2.29 |
| 50 | 3.70 |
| 100 | 5.57 |
| 200 | 7.73 |
| 500 | 13.00 |
| 1000 | 18.09 |
理论边界N*≈3.1(SNR=1处),实际扫到的最低N=20时SNR已是2.29。 这意味着在本实验的参数区(σ_g≈0.02、h²·S≈0.012)里,漂变几乎赢不了选择:哪怕20个成员的群落,选择信号也已经是漂变噪声的2.3倍。N=100时SNR=5.57,选择可靠主导。
N*≈3.1是理论边界,但种群大小N必须≥1,且实际种群不可能只有3个个体。 在本参数区(σ_g≈0.02、h²·S≈0.012),漂变主导区(N<N*)在生物学上不可达------这是本篇最大的诚实点。
N*低的原因是σ_g很小(0.0209)而h²·S相对大(0.012)------遗传方差供给少、选择力强,漂变的绝对量级σ_g/√N在任何可行N下都压不过选择响应。漂变要主导,得σ_g更大或h²·S更小,这是下一步扫σ_v×N双网格的活。
七、结论
| 问题 | 答案 |
|---|---|
| Δ*均值随N变吗? | 不变(≈0.005)。选择响应是确定性力,N只影响确定程度 |
| Δ*std怎么随N缩? | 按1/√N缩放(0.00478→0.00070),std/均值与1/√N同比例收缩 |
| 漂变何时淹没选择? | 理论边界N*≈3.1(SNR=1处);本参数区N=20时SNR已=2.29,漂变几乎赢不了 |
| 选择何时可靠主导? | h²>0.5(184-2遗传力侧)+ N>N*(185-2种群侧) |
叙事弧:
- 182-1:变异存续条件 σ_v > 0
- 183-1:选择生效条件 h² > 0.5(半效界)
- 184-2:h²=0.5是平滑过渡,公式全局适用
- 185-1:S闭式解从景观先验算出,独立预测成立
- 185-2:漂变定量化,"选择何时主导"完整两面 = 遗传力侧(h²>0.5) + 种群侧(N>N*)
降调:
-
N*≈3.1是σ_v=0.02单点扫描的结论。 σ_v增大→σ_g增大→N*上移(漂变噪声基数变大)。要看清漂变主导区,需要扫σ_v×N双网格------这是下一步的债。
-
std/均值与1/√N标度同比例收缩(比值在3.6--5.1间波动),但未严格等于1/√N。 偏差来自每格点σ_g实测微差(0.0196--0.0210间波动)和选择对漂变的非线性压缩,不是错误。
-
N=20时h²=0.4773略低于0.5------小种群下遗传方差估计本身受漂变干扰,h²侧边界和N侧边界在20人量级会互相污染,解读N=20行时要谨慎。
-
单位点模型。 多位点、上位效应、连锁不平衡待后续。
-
SNR基于稳态Δ*计算。 模拟跑500代到稳态后取值,有限世代下Δ*仍在波动,SNR是稳态量不是瞬态量。
系列还在逐步建设中,完善是和伙伴们的努力方向。
附录:完整代码
python
# -*- coding: utf-8 -*-
"""
天赐范式第185天:让漂变开始定量------扫N看选择主导边界 V3.3.23.0
接续184-2(h²侧回答"选择何时主导"),补N侧。
有限种群漂变每代注入σ_g/√N噪声,选择每代推h²·S------两者相当时选择信号被淹没。
扫N(20/50/100/200/500/1000),看多seed间Δ*离散度随N怎么缩、选择主导边界N*在哪。
预测:N大→std小→选择主导;N小→std大→漂变主导
边界N*:h²·S ≈ σ_g/√N* → N* ≈ (σ_g/(h²·S))²
选择机制:高斯加权(与185-1一致)
"""
import sys
import math
import numpy as np
if hasattr(sys.stdout, 'reconfigure'):
sys.stdout.reconfigure(encoding='utf-8')
PID = "TC-185-V3.3.23.0"
TARGET = 0.5
FITNESS_PEAK = 0.8
FITNESS_WIDTH = 0.10
BETA = 0.3
SIGMA_E = 0.02
N_GENERATIONS = 500
N_SEEDS = 50
SIGMA_V = 0.020
N_GRID = [20, 50, 100, 200, 500, 1000]
def bar(title):
print("=" * 72)
print(" " + title)
print("=" * 72)
print()
def sub(title):
print("【" + title)
print("-" * 72)
def fitness(x):
return np.exp(-(x - FITNESS_PEAK) ** 2 / (2 * FITNESS_WIDTH ** 2))
def run_one(n_pop, seed):
rng = np.random.RandomState(seed)
genes = rng.normal(TARGET, 0.01, n_pop)
for gen in range(N_GENERATIONS):
phenos = genes + rng.normal(0, SIGMA_E, n_pop)
fits = fitness(phenos)
probs = fits / fits.sum()
sel_idx = rng.choice(n_pop, size=n_pop, p=probs)
parent_genes = genes[sel_idx]
genes = TARGET + BETA * (parent_genes - TARGET) + rng.normal(0, SIGMA_V, n_pop)
var_g = float(np.var(genes))
mean_g = float(np.mean(genes))
h2 = var_g / (var_g + SIGMA_E ** 2)
phenos = genes + rng.normal(0, SIGMA_E, n_pop)
fits = fitness(phenos)
mean_parents_x = float(np.mean(phenos))
weighted_x = float(np.average(phenos, weights=fits))
S = weighted_x - mean_parents_x
delta_star = mean_g - TARGET
return h2, delta_star, S, var_g, mean_g
def main():
bar(f"{PID} 让漂变开始定量------扫N看选择主导边界")
print(f"模型: x=g+e, g'=TARGET+β(g_sel-TARGET)+v")
print(f"选择机制: 高斯加权(与185-1一致)")
print(f"固定: β={BETA}, σ_e={SIGMA_E}, σ_v={SIGMA_V}, gen={N_GENERATIONS}, seeds={N_SEEDS}")
print(f"fitness峰: x*={FITNESS_PEAK}, ω={FITNESS_WIDTH}, TARGET={TARGET}")
print(f"扫描: N = {N_GRID}")
print(f"预测: 漂变噪声≈σ_g/√N, 选择响应≈h²·S, 边界N*≈(σ_g/(h²·S))²")
print()
sub("N扫描结果(多seed均值±std)")
print(f"{'N':>6s} {'h²':>8s} {'Δ*':>12s} {'S':>10s} {'σ_g':>8s} {'漂变噪声':>10s} {'选择响应':>10s} {'信噪比':>6s}")
print("-" * 80)
results = []
for n_pop in N_GRID:
h2_list = []
delta_list = []
S_list = []
var_g_list = []
for seed in range(N_SEEDS):
h2, delta, S, var_g, mean_g = run_one(n_pop, seed)
h2_list.append(h2)
delta_list.append(delta)
S_list.append(S)
var_g_list.append(var_g)
h2_mean = np.mean(h2_list)
delta_mean = np.mean(delta_list)
delta_std = np.std(delta_list)
S_mean = np.mean(S_list)
var_g_mean = np.mean(var_g_list)
sigma_g = math.sqrt(var_g_mean)
drift_noise = sigma_g / math.sqrt(n_pop)
selection_response = h2_mean * S_mean
snr = selection_response / drift_noise if abs(drift_noise) > 1e-10 else float('nan')
results.append({
'n_pop': n_pop,
'h2_mean': h2_mean,
'delta_mean': delta_mean, 'delta_std': delta_std,
'S_mean': S_mean,
'sigma_g': sigma_g,
'drift_noise': drift_noise,
'selection_response': selection_response,
'snr': snr,
})
print(f"{n_pop:>6d} {h2_mean:>8.4f} {delta_mean:>8.5f}±{delta_std:.5f} {S_mean:>8.5f} {sigma_g:>8.4f} {drift_noise:>10.6f} {selection_response:>10.6f} {snr:>6.2f}")
print()
sub("漂变 vs 选择:Δ*离散度随N缩放")
print(f"{'N':>6s} {'Δ*均值':>10s} {'Δ*std':>10s} {'std/均值':>8s} {'1/√N标度':>10s}")
print("-" * 52)
for r in results:
cv = r['delta_std'] / abs(r['delta_mean']) if abs(r['delta_mean']) > 1e-10 else float('nan')
inv_sqrt_n = 1.0 / math.sqrt(r['n_pop'])
print(f"{r['n_pop']:>6d} {r['delta_mean']:>10.5f} {r['delta_std']:>10.5f} {cv:>8.3f} {inv_sqrt_n:>10.4f}")
print()
sub("选择主导边界N*")
r_ref = results[-1]
sigma_g_ref = r_ref['sigma_g']
h2_ref = r_ref['h2_mean']
S_ref = r_ref['S_mean']
N_star = (sigma_g_ref / (h2_ref * S_ref)) ** 2
print(f"用大N参考值估算边界:")
print(f" σ_g = {sigma_g_ref:.4f}")
print(f" h² = {h2_ref:.4f}")
print(f" S = {S_ref:.5f}")
print(f" h²·S = {h2_ref * S_ref:.6f}")
print(f" N* = (σ_g/(h²·S))² = {N_star:.1f}")
print()
print(f"信噪比随N变化:")
for r in results:
bar_str = "█" * int(r['snr']) if r['snr'] < 50 else "█" * 50
print(f" N={r['n_pop']:>5d}: SNR={r['snr']:>6.2f} {bar_str}")
print()
sub("结论")
print(f"184-2:h²侧回答'选择何时主导'(h²>0.5,平滑过渡)")
print(f"185-2:N侧回答'选择何时主导'")
print(f" 漂变噪声≈σ_g/√N,选择响应≈h²·S")
print(f" 边界N*≈{N_star:.0f}(信噪比=1处)")
print(f" N>N*时选择主导,N<N*时漂变主导")
print()
print(f" '选择何时主导'完整两面:遗传力侧(h²>0.5) + 种群侧(N>N*)")
if __name__ == "__main__":
main()

天赐范式 V3.3.23.0 · 2026-10-04