列表随机打乱与随机抽取:抽奖分组点名都靠它
每行一项粘进来,就能把列表随机打乱、随机抽 N 个、分成 K 组或抽出一个获胜者。用 Fisher-Yates 公平洗牌,随机源走浏览器本地的密码学随机数,抽奖分组点名都不偏心。
列表随机打乱与随机抽取:抽奖分组点名都靠它
需要从一份名单里随机抽几个人,或者把一串选项打乱顺序,大多数人第一反应是手动挑,或者随便点一个。问题是,只要是人来选,旁边总有人觉得不公平。把这件事交给算法,才能让结果谁都没话说。这篇讲清楚列表随机这件小事背后的门道:怎么算"公平",哪些场景用得上,以及为什么随机结果不该写进分享链接。
列表随机到底在做什么
逻辑很朴素:每行一项粘进来,工具把它当成一份字符串列表,然后做四件事之一。
- 打乱:把整份列表重排成一个随机顺序,每项只出现一次。
- 抽 N 个:从列表里随机取出 N 项,默认不重复(无放回)。
- 分 K 组:先公平打乱,再轮流把人发进 K 个桶,各组人数尽量均衡。
- 抽 1 个:从列表里掷出单个获胜者,可配揭晓动画。
关键在于"每行一项"。逗号分隔不行,"alice, bob, carol"写在一行会被当成一个超长的项。从表格里复制一列粘进来,会自动按换行对齐。
Fisher-Yates 为什么是公平的
很多页面用一招偷懒的写法:给列表加一个随机比较器去排序。这招看着像打乱,实际上已经被证明有偏,某些排列出现的概率明显更高。我们不那么干。
这里用的是 Fisher-Yates(又叫 Knuth)洗牌。它的核心保证是:对一份 N 项的列表,N! 种可能排列里,每一种出现的概率完全相等。做法是从最后一项往前走,每一步在"当前位置到开头"这段区间里等概率挑一个下标交换。只要每一步的区间抽样都均匀,整份打乱就严格无偏,没有哪个顺序被悄悄偏袒。
随机源也讲究。区间抽样如果直接写 Math.floor(Math.random() * n),在非 2 的幂的区间上会引入取模偏差,靠前的几个下标会略微更容易被抽到。这里的随机数来自 crypto.getRandomValues,也就是浏览器给安全场景用的那个密码学级随机数,再配合拒绝采样把它干净地映射到每个下标区间,取模偏差被彻底消掉。
一个真实例子:27 人分 6 组
举个我自己常碰到的场景。带工作坊时有 27 个学员,要分 6 个人数均衡的项目组。手动分总有人嘀咕"凭什么我跟他一组"。
我把 27 个名字粘进 列表随机器,选"随机分组",K 设 6。工具先做一次公平打乱,再轮流发牌进 6 个桶,出来的人数是 5、5、5、4、4、4。因为分组前已经洗过一遍,谁也没法从花名册上的原始顺序猜到自己会落到哪组。每组整块复制进白板就行。觉得搭配不顺手就重掷,每次都从头重新打乱,不存在"上次那个分法的残留"。
换个抽样的例子也一样好使:导出 800 张工单,主管让你随机读 25 张做质量抽检。选"抽 N 个",N 设 25,不勾重复,你拿到的是 25 个无放回抽出的工单号,一份正经的简单随机样本,而不是"前 25 个"或"每隔 32 个取一个",后两者都可能藏着系统性偏差。
为什么分享链接不锁定获胜者
这是个容易踩的坑。工具把你的输入列表写进 URL,所以分享链接能复现同一份初始列表和你选的模式。但随机结果(打乱顺序、抽取、分组、获胜者)绝不写进链接,每次有人运行都重新掷一遍。
这是故意的。如果把"获胜者"烤进分享 URL,造链接的人就能反复试到自己想要的结果再发出去,公平性就没了。所以要一个大家都认的获胜者,正确做法是当场抽一次截图,或者直播里录屏当证据。别发个链接,然后假设对方看到的和你一样。
本地随机,数据不出页面
每一步运算,从 Fisher-Yates 打乱到 crypto.getRandomValues 抽取,都是在你浏览器标签里跑的纯 JavaScript。列表、名字、抽奖号、抽取结果都不会发到服务器,不记录,也不用于分析。唯一会离开页面的是你主动分享时编进 URL 的那份输入列表。名单私密的话,手动复制结果文本就好,别分享链接。
如果你的需求其实只是要一个区间内的随机数字,而不是在一串文本里挑,那用 随机数生成器 更直接。手上是一串名字、选项或任务想重排或从中挑选,列表随机器才是对的那个。
抽奖、随机分组、排值班表、群里点名定个主意,这些场景共同的诉求只有一个:让结果中立到谁都挑不出毛病。公平的洗牌算法加上本地的密码学随机,正好把这件事办干净。
Made by Toolora · Updated 2026-06-13