# 教材位置：第1章，第1.7节；图 1-2。
# 单位、变量与形状：a、b、next_a 为 (8,) 的点数数组；点数与概率均无量纲。
# 学习目标：认识 NumPy 的随机数生成器、随机种子和离散均匀抽样，理解怎样复现实验。
# 核心关系：公平骰子满足 P(X=k)=1/6，k=1,...,6；一次抽样得到的是随机样本，
# 不能因为本次某个点数出现较多，就认定其理论概率较大。
# 数据与输出：构造两个具有相同初始状态的生成器，各取 8 个骰子点数，再展示后续抽样。
# 运行方式：安装 NumPy、Matplotlib 后执行 python e01_rng.py；输出终端结果和教材图
# e01_rng.png。

# 使用 np 这个惯用别名，便于调用数组运算和随机数工具。
import numpy as np

# default_rng 返回独立的生成器对象；2026 是种子，不是样本量或概率参数。
# 两个对象从相同种子出发，并执行相同的调用时，会产生相同的伪随机序列。
# 这便于排查代码差异；跨 NumPy 版本或更换算法时，不应无条件承诺序列仍相同。
rng_a = np.random.default_rng(2026)
rng_b = np.random.default_rng(2026)
# integers 的下界 1 包含在内，上界 7 不包含在内，因此可能值恰好是 1 到 6。
# size=8 生成形状为 (8,) 的一维整数数组，而不是形状为 (8, 1) 的列向量。
a = rng_a.integers(1, 7, size=8)
# rng_b 也取自己的前 8 个结果，因此 b 应与 a 逐元素相同。
b = rng_b.integers(1, 7, size=8)
# 生成器会记录并推进内部状态；这里继续使用 rng_a，不会自动从种子重新开始。
# 后续样本可能与前一批有重复点数，这是随机抽样的正常现象。
next_a = rng_a.integers(1, 7, size=8)
# 三行输出依次对比首次抽样、相同种子的首次抽样、同一生成器的下一次抽样。
print("首次抽样：", a)
print("相同种子的首次抽样：", b)
print("同一生成器的下一次抽样：", next_a)
# array_equal 同时检查形状及所有对应元素，验证本例的可复现性。
assert np.array_equal(a, b)
# 数组比较得到布尔数组；& 逐元素合并两个条件，all 要求所有点数都在合法区间。
# 两边条件必须加括号，避免 Python 的运算优先级改变原意。
assert np.all((1 <= a) & (a <= 6))

# 教材图 1-2：绘制公平骰子的完整理论分布，不把上面8次抽样的频率当作理论概率。
# pyplot 仅负责作图；这里不调用随机数生成器，因此不会改变原随机序列和终端输出。
import matplotlib.pyplot as plt
# 在创建第一幅图之前配置可跨系统选择的中文字体。
from plot_style import configure_chinese_plot

configure_chinese_plot()

# dice_values 是随机变量 X 的完整支持集；理论 概率质量函数 为每个点数对应的 1/6。
dice_values = np.arange(1, 7)
dice_pmf = np.full(dice_values.size, 1 / 6)
# 在0与7之间画完整累积分布函数；where='post'使每个整数右侧保持当前累计值，体现右连续性。
cdf_x = np.arange(0, 8)
cdf_y = np.r_[0.0, np.cumsum(dice_pmf), 1.0]
fig, axes = plt.subplots(1, 2, figsize=(6.3, 2.8), constrained_layout=True)
# 左图的纵轴是单点概率；六根柱高之和为1。
axes[0].bar(dice_values, dice_pmf, width=0.55)
axes[0].set(title='公平骰子的概率质量函数', xlabel='骰子点数 k', ylabel='P(X = k)',
            xticks=dice_values, ylim=(0, 0.21))
# 右图实心点表示F(k)，空心点表示从左侧到达k时尚未计入该点质量的极限。
axes[1].step(cdf_x, cdf_y, where='post')
axes[1].scatter(dice_values, np.cumsum(dice_pmf), s=18, zorder=3)
axes[1].scatter(dice_values, np.r_[0.0, np.cumsum(dice_pmf)[:-1]],
                s=18, facecolors='white', edgecolors='C0', zorder=4)
axes[1].set(title='右连续的累积分布函数', xlabel='阈值 x', ylabel='P(X <= x)',
            xticks=dice_values, xlim=(0, 7), ylim=(-0.03, 1.05))
# 此文件是教材实际嵌入图，运行脚本后直接采用，不另行重新抽样或独立重绘。
fig.savefig('e01_rng.png', dpi=180, bbox_inches='tight')
plt.close(fig)
