随机
实施正确随机—稳定分配、平衡桶、无 carryover 效应 —使处理和对照组可比且实验量因果非相关
为什么
随机化是使实验因果之物:若用户随机分处理和对照则两组(期望上)除处理外每方面同且结果的任差由处理致。破随机化则实验量组间既有差异非处理效应。最常见破是不稳分配:用户周一在处理、周二在对照(因 hash 变或重跑分配)且其行为被两条件污。修是稳分配:把 user_id hash 到桶一次并存故用户全实验同臂。 第二破是不平衡—0/50 分因 bug —60% 高价值用户放处理且处—因有更好用户非因处理更好。修是平衡随机化(块随机化或分层随机化)确保臂在关键维平衡。第三破—carryover(干扰):处理中用户告对照中用户功能且对照用户行为被污;此在社交—B2B 产品中常见其中用户协作且修是簇随机化(按公司非用户随机)或地理实验(按地区随机)
何时使用
当实任何 A/B 测时。当用户报见两变体时(不稳分配)。当处理组基线指标看似异于对照时(不平衡)。当用户互互时(carryover 风险)
如何做
1. 用稳分配:把 user_id —experiment_id hash 到桶—-99)并存分配到查表 —用户全实验同臂、即使清 cookie 或换设备 2. 用确定—hash(SHA-256 —MurmurHash3)非 Math.random() —Math.random() 每调给不同值、产不稳分配—user_id, experiment_id) —hash 确定且稳 3. —hash 桶分臂:—< 50 -> 对照、桶 >= 50 -> 处理—0/50 分)—0/20 分则—< 80 -> 对照、桶 >= 80 -> 处理 4. 分配后验平衡:查处理和对照组有相似基线(实验前转化率、ARPU、任期)—若差—1 标准误则随机化破且实验无—5. 小样本用块随机化:若样本小(每臂 < 1,000)则简单随机化可偶然产不平衡;块随机化— 块分:每—2 对照 2 处理)保平衡 6. 异质群用分层随机化:若群有不同段(免—vs 付费、美 vs 国际)则每段内分层故两臂同段组合 —此减方差增功—7. 用簇随机化处—carryover(干扰):若用户互(B2B、社交)则按簇(公司、工作区)随机非按用—公司所有用户同臂故无跨臂污 8. 网络效应产品用地理实验:按地区(—vs —vs 亚)或城市随—此避处理中用户通过网络影响对照中用户的问题 9. 排除可能暴露两臂的用户:员工、QA 测试、换账号用户 —此用户污两臂且应从分析排—10. 记分配:每用户分配(user_id、experiment_id、arm、assigned_at)记故分析可验稳分配并复实验
示例
# 随机化:EXP-247(试用长—实验 ID:EXP-247
分:50/50(对照:14 天、处理:7 天)
分配函数(TypeScript):
function assignArm(userId: string, expId: string): 'control' | 'treatment' {
const hash = murmurhash3(userId + expId); // 确定— const bucket = hash % 100; // 0-99
return bucket < 50 ? 'control' : 'treatment';
}
// 存于 experiment_assignments 表:
// (user_id, experiment_id, arm, assigned_at)
// ON CONFLICT DO NOTHING -- 稳分—
平衡查(实验前— 天基线)— 指标 | 对照 | 处理 | — | SE | OK?
-------------------|---------|----------|------|-------|----
前转— | 3.18% | 3.22% | 0.04%| 0.12% | — —ARPU | $24.40 | $24.60 | $0.20| $0.80 | — 任期(月— | 4.2 | 4.3 | 0.1 | 0.3 | — —% | 62% | 61% | 1% | 3% | — 付费 % | 15% | 15% | 0% | 2% | — -> 所有差 < 1 SE、随机化平衡
Carryover 处理— - B2B 用户(company_id != null):—company_id 簇随机化
故公司所有用户同— - 消费用户(company_id == null):个体随机— - 分配函数— if (user.companyId) {
return assignArm(user.companyId, expId); // " } else {
return assignArm(user.id, expId); // 个体
}
排除— - user_id < 1000(内—种子账号— - email_domain IN ('test.dev', 'internal.ez4code.com')
- —30 天换账号用户(跨臂风险)
分配日志(样):
user_id | exp_id | arm | assigned_at
--------|----------|-----------|---------------------
1234 | EXP-247 | control | 2026-09-22 00:01:00
1235 | EXP-247 | treatment | 2026-09-22 00:01:00
1236 | EXP-247 | control | 2026-09-22 00:01:00
(每—38,400—8 天实验稳)常见问题
- ⚠—Math.random() 分配 —它每调给不同值故用户页载 1 在处理、页—2 在对照;分配不稳、行为被污且实验测噪
- ⚠不存分配 —每请求重—hash 且若 hash 函数—user_id 变(账号合、cookie 清)则用户实验中换臂且其数据不可析
- ⚠无平衡查 —随机—bug —60% 付费用户放处理、处理在收入—且团队发不生效的变更;平衡查(实验前基线)在实验开始前捕此
- ⚠B2B 产品中个体随机化 —处理中用—A 告同公司对照中用—B 功能且对照行为被污;簇随机化(按公司)防协作产品的跨臂污
- ⚠含员工和 QA 测试于实—他们见两臂(因测两)、其行为异于真用户且污两臂;分配前按 user_id 或邮箱域排除他们
最佳实践
- ✓—(user_id, experiment_id) 的确定—hash(MurmurHash3 —SHA-256)分配并存结果到查表 —此保稳分配(用户全实验同臂)即使 cookie 清或设备变
- ✓分配后通过比臂间实验前基线(转化率、ARPU、任期)验平—若任指标差超 1 标准误则随机化破、实验无效且分配须在上线前调
- ✓B2B 和协作产品用簇随机化(按公司或工作区)—用户跨臂互时个体随机化致跨臂污且簇随机化保公司所有用户见同臂
- ✓异质群(免费 vs 付费、美 vs 国际)用分层随机—每段内分层确保两臂同段组合、此减方差增实验功率
- ✓从实验排除员工、QA 测试和换账号—他们见两臂、行为异于真用户且污两臂;排除列(按 user_id 或邮箱域)须在分配前用