使用方法
- 在「投掷次数 n」填 1 ~ 10000 之间的整数。示例值 1000——这是本页能给出有意义卡方结论的最小实用规模。
- 在「每组投掷次数」填 10 ~ 200 的整数,示例值 50。分组是为了观察"局部是否偏斜",不分组只看总频率会掩盖前半段和后半段的差异。
- 在「随机源」二选一。默认
crypto.getRandomValues;换成Math.random是为了让你亲眼看到两者在本测试下分布没有区别,但它们的可预测性完全不同。 - 点「开始投掷」。页面会一次性取回 n 个 uint32,逐个取最高位当作正反面,然后给出频率、标准差、z 值和卡方结论。
- 下方分组对照表默认只显示 12 行,点「加载更多分组」逐步展开;需要离线核对时用「导出 CSV」,文件带 BOM,Excel 直接双击不乱码。
这个抛硬币模拟能解决什么问题
抛硬币模拟的真正用途不是"替你决定",而是回答一个具体问题:看到一堆正反结果,能不能说它偏向某一面。生活里这类判断天天出现——群里讨论午饭选哪家、投票前估一下票数、实验记录里判断一批样品有没有系统偏差、一个"随机"排序是不是其实有倾向。凭感觉看会出错:1000 次里出现 530 次正面,直觉上像"硬币偏了",但统计上完全正常。
它也能当反面教材。同一批数据在 n=10 和 n=10000 下结论完全相反,这正是统计推断的核心:样本量决定了你有多大概率说真话。抛硬币模拟把这个过程做成可重复的实验,你能亲眼看着频率从 0.70 收敛到 0.50,也能看着某个分组跳出 95% 区间后又回归。
本页不做的事:不模拟物理硬币的转动与碰撞(不涉及任何力学方程),也不提供多面币、骰子或带权重的不公平硬币——那些是别的工具负责的。这里只做一件事:标准二项分布 B(n, 0.5) 的抽样与检验。
随机比特的取法与统计口径
页面调用的是 Web Crypto API 的 crypto.getRandomValues(new Uint32Array(n)),然后对每个元素取 (v >>> 31) & 1——uint32 的最高位,取 0 记反面、取 1 记正面。
- 为什么取最高位而不是
u % 2:两者都严格无偏,因为 uint32 有 232 个等概率取值,而 2 能整除 232。差别在实现——最高位是纯位运算,不做除法,也不怕除数为 0 的边界讨论。 - 为什么用 crypto 而不是 Math.random:Web Crypto 规范(W3C)要求
getRandomValues必须来自操作系统熵池,是密码学安全的;Math.random()由 V8 的 xorshift128+ 驱动,给够 624 个连续输出就能还原内部状态并预测后续全部结果。抛硬币模拟用 crypto 是因为它同时也是本站其它随机类工具的同一条熵源。 - 为什么一次取 n 个而不是循环 n 次:n=10000 时循环调用要 10000 次跨层往返,而单次
getRandomValues分配 10000 × 4 = 40000 字节缓冲区一次填满。规范对单次请求有 65536 字节上限,本页的 10000 次上限正好卡在这个额度之内。
统计量按二项分布 B(n, 0.5) 计算。因为 p(1−p) = 0.25,所以标准差是 σ = 0.5 × √n 而不是 √n。z 分数 = (H − n/2) / σ,H 为正面次数。
卡方拟合优度检验用 df=1 的形式 χ² = 2(H − n/2)² / (n/2),判断它是否超过临界值。实测临界值:α=0.05 → 3.8415,α=0.01 → 6.6349,α=0.001 → 10.8276。1.96σ 的近似区间是给不需要做检验时快速看的,两者在 n 大时几乎重合。
分组卡方是同一个公式作用在每组的 G 次上:G=50 时期望频数 E=25,落在卡方检验要求的 E ≥ 5 之上;单组正面数 ≤18 或 ≥32 时 χ² 才越过 3.8415。这条阈值是算出来的,不是拍的。
完整示例(数值仅作演示)
示例条件:n = 1000,每组 50 次(拆成 20 组),随机源 crypto.getRandomValues。
- 期望正面数 E = 500.0,标准差 σ = 0.5 × √1000 = 15.81。
- 95% 区间 = 500 ± 1.96 × 15.81 = [469.0, 531.0],也就是正面频率 46.90% ~ 53.10%。
- 若本次结果是 530 正 / 470 反:χ² = 3.6000,z = 1.8974。3.60 < 3.8415,不能拒绝公平性——这就是"看起来偏了但其实没偏"的典型数值。
- 若本次结果是 600 正 / 400 反:χ² = 40.0000,z = 6.3246,远超 3.8415,这时才该怀疑随机源或流程。
- 分组层面:单组 50 次里出现 18 正 χ²=3.9200(越界),19 正 χ²=2.8800(未越界)。20 组里按 α=0.05 期望正好有 1 组越界,看到一两行标红属于正常,不是程序出错。
以上数字用于演示检验逻辑。每次点「开始投掷」得到的具体序列都会不同,页面显示的是当次实测值,不预置任何固定结果。
投掷次数与 95% 区间对照表
同一枚公平硬币,样本量直接决定你能测出多小的偏差。半宽 = 0.98 / √n:
| 投掷次数 n | 期望正面数 n/2 | 标准差 σ | 95% 正面数区间 | 95% 频率区间 | 半宽 |
|---|---|---|---|---|---|
| 10 | 5 | 1.58 | 1.9 ~ 8.1 | 19.01% ~ 80.99% | ±30.99% |
| 20 | 10 | 2.24 | 5.6 ~ 14.4 | 28.09% ~ 71.91% | ±21.91% |
| 50 | 25 | 3.54 | 18.1 ~ 31.9 | 36.14% ~ 63.86% | ±13.86% |
| 100 | 50 | 5.00 | 40.2 ~ 59.8 | 40.20% ~ 59.80% | ±9.80% |
| 200 | 100 | 7.07 | 86.1 ~ 113.9 | 43.07% ~ 56.93% | ±6.93% |
| 500 | 250 | 11.18 | 228.1 ~ 271.9 | 45.62% ~ 54.38% | ±4.38% |
| 1000 | 500 | 15.81 | 469.0 ~ 531.0 | 46.90% ~ 53.10% | ±3.10% |
| 2000 | 1000 | 22.36 | 956.2 ~ 1043.8 | 47.81% ~ 52.19% | ±2.19% |
| 5000 | 2500 | 35.36 | 2430.7 ~ 2569.3 | 48.61% ~ 51.39% | ±1.39% |
| 10000 | 5000 | 50.00 | 4902.0 ~ 5098.0 | 49.02% ~ 50.98% | ±0.98% |
读法:n=10 时 7 正 3 反和 3 正 7 反都在正常范围内,连"80% 正面"都碰得到;n=10000 时 51% 正面就已经越界。半宽与 √n 成反比——想把误差压到 1% 以内,样本量得从 1000 涨到 10000,成本是 10 倍。
边界与精度
- 次数下限 1:n=1 时 σ=0.5,95% 区间算出来是 [−0.48, 1.48],覆盖了整个 [0, 1] 取值域,统计上零信息量。卡方检验要求期望频数 E ≥ 5,即 n ≥ 10 才有意义,n < 10 时页面只报描述性结论不做检验判定。
- 次数上限 10000:对应 40000 字节随机缓冲区,低于 Web Crypto 单次 65536 字节的规范额度。再往上不是不行,而是生成的序列字符串(n × 3 − 1 字符,上限时 29999 字符)会让分页表格失去意义。
- 随机源分辨率:uint32 有 232 = 4294967296 个取值,单步分辨率 2−32 = 2.328e−10。这个粒度远小于 1/n,所以单次抽取不存在可察觉的偏置。
- 频率显示精度:正面频率按 2 位小数、百分比按 2 位小数显示,统计量(σ / z / χ²)按 4 位小数显示。内部计算全程 float64,不做提前取整——
0.5 × Math.sqrt(1000)的结果 15.811388300841896 一直保留到显示环节才截断。 - 序列只显示前 200 个:n=10000 时全部渲染会生成 29999 个字符的 DOM 文本,页面只展示前 200 个记号并注明"已截断",完整序列用「复制序列」或「导出 CSV」拿。这是渲染策略,不是数据丢失。
- 分组参数:每组 10 ~ 200 次。取 200 时 n=10000 只拆 50 组;取 10 时拆 1000 组,表格行数上限由「加载更多」控制,不会一次性撑爆 DOM。
常见问题
连出 5 次正面之后,下一次该出反面了吧?
不会,而且这是最有名的一个错误。上一次结果不影响下一次,概率永远是 0.5。容易产生错觉是因为"5 连正"本身不算罕见——它的事前概率是 0.55 = 3.125%,也就是每 32 次里就会遇到一次。在 n=100 的序列里,至少出现一次 5 连正的概率高达 95.3%。所以"该出反面了"是对随机性的误读,不是对概率的计算。
我这 1000 次里出现 530 次正面,硬币是不是有问题?
不是。n=1000 的 95% 区间是 469.0 ~ 531.0,530 落在区间内;χ²=3.6000 也小于临界值 3.8415。正确的说法是"在 α=0.05 下没有证据拒绝公平性",而不是"证明了绝对公平"——统计检验永远无法证明零偏差,只能给出证据不足或证据充分这两个结论。
分组表里有几行标红了,是不是程序算错了?
不是。每组 G=50 时,单组正面数 ≤18 或 ≥32 就越过 5% 临界值(18 正的 χ²=3.9200)。但你是同时看 20 个组,α=0.05 × 20 = 1.0,意味着平均本来就有 1 组标红。逐个组做检验再汇总,家族错误率会远高于 5%。要正式检验整体偏差,请只看顶部的总卡方,不要数有几行标红。
把随机源换成 Math.random,结果会不一样吗?
在 n=1000 这种样本量下,看不出区别。两者都是等概率的,分布检验都会通过。区别在可预测性:V8 的 Math.random 走 xorshift128+ 生成器,连续取到 624 个 32 位输出后可以还原其内部状态,从而预测之后所有结果。抛硬币模拟默认给 crypto 源就是为了让这个页面的输出不可被反推。
n=10 时能不能用这个页面做判断?
能看,不能判。n=10 的 95% 区间是 1.9 ~ 8.1,也就是 80% 正面、20% 正面都在正常范围。卡方检验的可靠性要求期望频数 E ≥ 5,n=10 刚好压线;n=20 时 E=10 才开始稳。要检测"硬币是否偏向某面"这种量级的差异,最低建议 n=1000——对应 ±3.10% 的分辨率。
页面里那条"最长连正约等于 log2(n)"是怎么来的?
几何分布的经典结论:长度 k 的正面段在 n 次里出现一次的概率约 n/2k+1,令它等于 1 解出 k ≈ log2(n)。代入可得 n=100 → 6.64、n=1000 → 9.97、n=10000 → 13.29。所以 n=10000 时看到 13 连正属于常态,不说明任何异常;判断异常仍然要看总卡方。
CSV 导出为什么开头有一段看不见的字符?
那是 UTF-8 BOM(U+FEFF),长度为 3 字节。Excel 打开不带 BOM 的 UTF-8 CSV 会把中文识别成乱码,加上 BOM 就能正确识别编码。它只影响 Excel 这类按 BOM 猜编码的软件,pandas.read_csv 默认也会跳过它,awk 则不会——这是刻意的取舍,不是 bug。
这个工具免费吗?数据安全吗?
免费,无需注册即可使用。随机源是浏览器内置的 crypto 熵池,不上传任何内容;页面上唯一的输入是次数和分组参数,你可以断网后刷新验证,开发者的 Network 面板会是空的。