2025-10-20 09:53:02 +08:00
|
|
|
import numpy as np
|
|
|
|
|
import matplotlib.pyplot as plt
|
2025-10-23 10:29:27 +08:00
|
|
|
from scipy.stats import beta as beta_dist # 导入beta分布用于绘图
|
2025-10-20 09:53:02 +08:00
|
|
|
|
2025-10-23 10:29:27 +08:00
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
# 1. 设置 matplotlib 支持中文显示
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
try:
|
|
|
|
|
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows/Linux
|
|
|
|
|
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
|
|
|
|
|
except Exception:
|
|
|
|
|
try:
|
|
|
|
|
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # MacOS
|
|
|
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
|
|
|
except Exception:
|
|
|
|
|
print("未找到中文字体,绘图可能显示异常。请安装'SimHei'或'Arial Unicode MS'字体。")
|
2025-10-20 09:53:02 +08:00
|
|
|
|
2025-10-23 10:29:27 +08:00
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
# 2. 设定模型参数 (为了模拟您图中的效果)
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
n = 20 # X的试验总次数
|
|
|
|
|
# 更改 alpha 和 beta 以匹配图中 ~0.72 的均值
|
|
|
|
|
alpha = 8.0 # Beta分布的先验参数 alpha
|
|
|
|
|
beta = 3.0 # Beta分布的先验参数 beta
|
|
|
|
|
# 理论均值 E[Y] = alpha / (alpha + beta) = 8 / 11 ≈ 0.727
|
|
|
|
|
theoretical_mean = alpha / (alpha + beta)
|
|
|
|
|
|
|
|
|
|
# MCMC (Gibbs) 抽样参数
|
|
|
|
|
N_samples = 1000 # 总抽样量 N (同您图中的 N=1000)
|
|
|
|
|
N_burn_in = 200 # 预估的老化期(预热期) N1
|
|
|
|
|
|
|
|
|
|
print(f"模型参数: n={n}, alpha={alpha}, beta={beta}")
|
|
|
|
|
print(f"理论均值 E[Y]: {theoretical_mean:.4f}")
|
|
|
|
|
print(f"抽样设置: 总样本 N={N_samples}")
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
# 3. 初始化
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
# 创建数组来存储所有样本
|
|
|
|
|
samples_X = np.zeros(N_samples, dtype=int)
|
|
|
|
|
samples_Y = np.zeros(N_samples, dtype=float)
|
|
|
|
|
|
|
|
|
|
# 设定马尔可夫链的初始状态
|
|
|
|
|
# 故意设置一个远离均值(0.727)的初始值,以观察收敛
|
|
|
|
|
y_t = 0.1
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
# 4. 运行 Gibbs 抽样
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
print("开始Gibbs抽样...")
|
|
|
|
|
np.random.seed(101) # 使用和您图中一样的随机种子
|
|
|
|
|
|
|
|
|
|
for i in range(N_samples):
|
|
|
|
|
x_t = np.random.binomial(n, y_t)
|
|
|
|
|
y_t = np.random.beta(x_t + alpha, n - x_t + beta)
|
|
|
|
|
samples_Y[i] = y_t
|
|
|
|
|
samples_X[i] = x_t
|
|
|
|
|
print("抽样完成。")
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
# 5. 定义并计算逐步平均值 (Ergodic Mean)
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
def calculate_ergodic_mean(y_samples):
|
2025-10-20 09:53:02 +08:00
|
|
|
"""
|
2025-10-23 10:29:27 +08:00
|
|
|
计算逐步平均值 (累积平均值)
|
|
|
|
|
y_k_bar = (1/k) * sum(y_i for i=1 to k)
|
|
|
|
|
|
|
|
|
|
使用 np.cumsum() 可以高效实现
|
2025-10-20 09:53:02 +08:00
|
|
|
"""
|
2025-10-23 10:29:27 +08:00
|
|
|
n = len(y_samples)
|
|
|
|
|
# 1. 计算累积和 [y1, y1+y2, y1+y2+y3, ...]
|
|
|
|
|
s = np.cumsum(y_samples)
|
|
|
|
|
# 2. 创建 k 数组 [1, 2, 3, ...]
|
|
|
|
|
k_array = np.arange(1, n + 1)
|
|
|
|
|
# 3. 计算 avg[k] = s[k] / k
|
|
|
|
|
return s / k_array
|
2025-10-20 09:53:02 +08:00
|
|
|
|
2025-10-23 10:29:27 +08:00
|
|
|
# 计算所有 Y 样本的逐步平均值 (包括预热期)
|
|
|
|
|
ergodic_mean_Y = calculate_ergodic_mean(samples_Y)
|
2025-10-20 09:53:02 +08:00
|
|
|
|
2025-10-23 10:29:27 +08:00
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
# 6. 绘制逐步平均值图 (实现您图片中的效果)
|
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
|
print("正在绘制逐步平均值图...")
|
2025-10-20 09:53:02 +08:00
|
|
|
|
2025-10-23 10:29:27 +08:00
|
|
|
plt.figure(figsize=(10, 6))
|
|
|
|
|
plt.plot(np.arange(1, N_samples + 1), ergodic_mean_Y, label=r"逐步平均值 $\bar{y}_k$")
|
|
|
|
|
plt.axhline(theoretical_mean, color='red', linestyle='--', label=f"理论均值: {theoretical_mean:.4f}")
|
2025-10-20 09:53:02 +08:00
|
|
|
|
2025-10-23 10:29:27 +08:00
|
|
|
# 添加一个垂直线来标记我们估计的预热期
|
|
|
|
|
plt.axvline(N_burn_in, color='gray', linestyle=':', label=f"估计的预热期 N1 = {N_burn_in}")
|
2025-10-20 09:53:02 +08:00
|
|
|
|
2025-10-23 10:29:27 +08:00
|
|
|
plt.title("使用逐步平均值图查看预热期")
|
|
|
|
|
plt.xlabel("迭代次数 (k)")
|
|
|
|
|
plt.ylabel(r"逐步平均值 $\bar{y}_k$")
|
|
|
|
|
plt.legend()
|
|
|
|
|
plt.grid(True)
|
|
|
|
|
plt.ylim(0, 1) # Y值在0到1之间
|
|
|
|
|
plt.show()
|