盘面早就知道了
为什么中奖条目在动画开始之前就已抽定,以及这个顺序换来了什么。
按下 Spin,盘面转十秒。看起来答案是在这十秒里某个时刻定下来的——在减速里,在最后半圈里, 在指针差一点就多越过一条分隔线的那个瞬间。
不是的。中奖条目在几微秒内先抽出来,动画随后才被瞄准它。
为什么是这个顺序
反过来的做法很明显:让盘随便转,停在哪就算哪。听上去更公平——没人选,是物理选的—— 但它反而更难辩护,因为它把每一个关于公平的问题,都变成了一个关于动画的问题。
用力甩能不能甩出别的答案?旋转中途点一下让它提前落定,会不会有偏?
开了 prefers-reduced-motion、根本看不到动画的人,看到的又是什么?
先抽再转,这三个问题的答案是同一个:不会,因为抽取已经发生过了。 甩的力度换来更多圈数,提前点击缩短行程,减弱动效则直接跳过行程、立刻播报结果。 三条不同的路径走的是同一段代码,而不是三个各自要单独论证的分支。
抽取用的是什么
熵源是 crypto.getRandomValues——浏览器自带的加密随机数生成器——绝不是 Math.random。
从它拿到一个均匀分布的数,比看上去多一步。最直觉的 draw % count 只要 count 除不尽 2³²
就是有偏的:排在前面的那几个条目,各自会多拿到四十亿分之一的机会。很小,
但那是一根压在秤上的手指,而一个声称公平的转盘不该有这种东西。
所以落在余数尾巴里的抽取会被丢掉,重抽一次。
const limit = Math.floor(2 ** 32 / max) * max;
let draw = crypto.getRandomValues(new Uint32Array(1))[0];
while (draw >= limit) {
draw = crypto.getRandomValues(new Uint32Array(1))[0];
}
return draw % max;
代价是偶尔多抽一次,换来的是严格均匀的结果。同一段代码就摆在 公平性页面上——在那里它本身就是正文,而不是正文的插图。
权重是概率,不是偏爱
给一个条目权重 3,它在抽取里就占三份而不是一份。盘面把它画成更宽的扇区, 名单则直接显示这个权重换来的百分比,不用谁自己去除。
两个文字相同的条目仍然是两个条目。它们各自保留自己的机会, 名单只会告诉你发现了几处重复,而不会悄悄把它们合并—— 不打招呼就把某个人的中奖概率砍掉一半,是没人要的那种「贴心」。
那动画还有什么用
被瞄准的动画不等于假动画。盘面真的转到了它落定的那个扇区, 每一声咔哒真的对应它掠过的一条分隔线,擦边悬停——如果你打开它—— 悬的是中奖条目的邻居而不是随便一个扇区,然后继续走到那个在一切开始之前就抽定的条目上。
悬念是真的。只是结果比它看上去要老一点。