代表性面试主题

如何实现 Vose Alias Method 做 O(1) 加权随机采样?

编程题困难
OfferCC 编辑团队发布 更新

题干

给定 N 个带非负权重的选项,需要反复按权重比例随机选取一个选项。请实现 Vose Alias Method,说明预处理与采样复杂度、概率证明、权重更新和如何验证长期频率。

1. 题目

广告系统有 N 个候选项,每个选项的权重表示被抽中的相对概率。初始化后会执行数百万次单项采样,要求每次采样尽量为 O(1),并允许权重批量更新。请设计并实现 alias table,说明零权重、浮点误差和随机数边界。

2. 约束与澄清

  • 先讨论有放回的单项采样;不放回抽样和动态单项更新是延伸。
  • 权重必须非负且总和大于零;零权重选项不应被采到。
  • 采样器可以使用均匀随机整数和 [0, 1) 的均匀随机数。
  • 权重变化后可以接受 O(N) 重建,但不能假装旧表仍代表新分布。

3. 核心思路

把每个权重归一化为 p_i = w_i * N / sum(w),平均值为 1。维护长度为 N 的 prob 和 alias 数组:每个桶先以概率 prob[i] 返回自身,否则跳转到 alias[i]。预处理时将小于 1 的桶放入 small,大于 1 的桶放入 large;从两边取出一对,把大桶剩余容量填入小桶,直到所有桶完成。

采样先均匀选桶 i,再用一次均匀小数与 prob[i] 比较。每个原始选项在若干桶中占据的面积总和等于其归一化概率,因此长期频率与目标权重成比例。

4. 参考实现

text
build(weights):
  n = len(weights)
  scale = n / sum(weights)
  scaled = [w * scale for w in weights]
  prob = array(n)
  alias = array(n)
  small, large = [], []
  for i, value in enumerate(scaled):
    (small if value < 1 else large).append(i)

  while small and large:
    s = small.pop()
    l = large.pop()
    prob[s] = scaled[s]
    alias[s] = l
    scaled[l] -= 1 - scaled[s]
    (small if scaled[l] < 1 else large).append(l)

  for i in small + large:
    prob[i] = 1
    alias[i] = i
  return prob, alias

sample(prob, alias, rng):
  i = rng.uniform_int(0, len(prob))
  return i if rng.uniform01() < prob[i] else alias[i]

5. 复杂度与正确性

预处理时间和空间都是 O(N);每次采样只需一次均匀桶选择、一次比较和最多一次数组访问,时间为 O(1)。prob 应被限制在 [0, 1],剩余浮点误差可在构建结束时钳位;随机整数上界必须明确为半开区间,避免最后一个桶被遗漏。

验证不能只抽几次看结果。应运行足够样本,比较每个选项的观测频率与目标 w_i / sum(w),使用置信区间或卡方检验发现明显偏差。重建时要原子替换整张表,避免采样线程看到混合版本。

6. 追问与陷阱

  • Alias Method 适合静态或批量更新分布;单个权重频繁变化时,Fenwick tree 或 segment tree 可能更合适。
  • 归一化前总权重溢出会破坏比例,应使用更宽精度或先缩放。
  • “O(1)”不包括重建成本,也不代表随机数生成器本身没有开销。
  • 非负权重总和为零时分布未定义,应拒绝输入,而不是平均返回所有选项。

7. 延伸阅读

可以比较前缀和二分、Fenwick tree、reservoir sampling 和 alias table:前缀结构支持动态更新但采样为 O(log N),reservoir 适合流式样本,alias table 则以 O(N) 预处理换取高吞吐 O(1) 采样。

8. 面试评分点

能构造 small/large 桶

应说明缩放权重、平均容量为 1,以及如何用小桶和大桶互相填充容量。

能证明采样概率

应解释“均匀选桶加一次别名跳转”如何让每个选项获得目标总面积,而不是只背代码。

能处理数值和边界

应覆盖零权重、总和为零、浮点钳位、随机整数半开区间和表版本原子替换。

能判断数据结构取舍

应比较批量重建与动态更新成本,说明何时选 Fenwick tree 或前缀和,而非盲目使用 alias table。

公开来源

同类题目

资源

产品对比

每份对比均基于官方资料,并标注复核日期。

查看完整对比

相关面试工具

用 Screenshot 处理算法题

截图题目后,按顺序看约束、解法、代码、边界条件和复杂度。

查看工具