中EN
概念页

别名法与水塘采样

Alias Method & Reservoir Sampling

更新于 2026-10-16 约 6 分钟别名法水塘采样离散采样ReSTIRPBRT

从 nn 个离散结果中按概率 pip_i 采样,线性扫描 CDF 每次 O(n)O(n),二分查找 O(log⁡n)O(\log n);别名法 (alias method) 预处理 O(n)O(n) 后每次采样 O(1)O(1):均匀选一个桶,再以桶内概率 qiq_i 决定取桶本身的结果还是它唯一的"别名"。水塘采样 (reservoir sampling) 则不需要预先知道结果个数、也不需要把所有候选存在内存里:候选逐个流过,每个以"自身权重 / 已见权重和"的概率替换水塘中的样本,最终保留的样本恰好服从全部候选的分布;两个水塘还可以无偏地合并。前者用于按功率选光源、环境贴图采样等,后者是 ReSTIR 等方法的基础。

别名法(PBRT 4e A.1)

  • 结构:nn 个桶,每个桶代表 1/n1/n 的概率质量。桶 ii 存 qiq_i 和别名下标:选中桶 ii 后,以概率 qiq_i 返回 ii,否则返回别名。可以证明"每个桶对应自身结果 + 至多一个别名"足以表示任意离散分布。所有 pip_i 相等时 qi=1q_i=1,退化为 ⌊nξ⌋\lfloor n\xi\rfloor。 别名表采样只需一次均匀掷骰:先选桶,再在桶内决定接受本桶结果还是走别名。
flowchart TD
  U["均匀随机数 u"] --> Bucket["桶下标 i = floor(n·u)"]
  Bucket --> Frac["小数部分决定接受"]
  Frac --> Q{"小于 q_i?"}
  Q -->|"是"| RetI["返回结果 i"]
  Q -->|"否"| RetA["返回别名 alias[i]"]
  • 例(表 A.1):p={1/2,1/4,1/8,1/8}p=\{1/2,1/4,1/8,1/8\},表为 桶 1:q=1q=1;桶 2:q=0.5q=0.5,别名 1;桶 3:q=0.5q=0.5,别名 1;桶 4:q=0.5q=0.5,别名 2。笔者逐项核算:P(1)=14+14⋅12+14⋅12=12P(1)=\tfrac14+\tfrac14\cdot\tfrac12+\tfrac14\cdot\tfrac12=\tfrac12,P(2)=14⋅12+14⋅12=14P(2)=\tfrac14\cdot\tfrac12+\tfrac14\cdot\tfrac12=\tfrac14,P(3)=P(4)=18P(3)=P(4)=\tfrac18,正确。(按下面的 Vose 算法,桶 2 的 p^=1\hat p=1,实际会得到 q2=1q_2=1 的另一张同样合法的表。)
  • 构建(Vose 1991):把概率乘以 nn 得 p^i=npi\hat p_i=np_i(平均值为 1),分成 p^<1\hat p<1 的 under 表和 p^≥1\hat p\ge1 的 over 表。每次各取一项 un、ov:桶 un 的 q=p^unq=\hat p_{un}、别名为 ov;ov 剩下 p^un+p^ov−1\hat p_{un}+\hat p_{ov}-1,按大小放回相应的表。每轮恰好消耗 1 单位质量、少一个桶,剩余桶的平均值仍为 1,所以总能继续直到结束,总时间 O(n)O(n)。浮点舍入可能使某一表剩下几项(值略大于或略小于 1),把它们设为 q=1q=1。
  • 实现细节:权重和用 double 累加(结果数多、量级差大时单精度会让构建出错);采样时 uu 只用于选桶,nu−⌊nu⌋nu-\lfloor nu\rfloor 又是一个均匀数,用它决定是否取别名,再把剩余部分重映射成新的均匀样本返回(与 随机变量变换与逆变换采样 中 SampleDiscrete() 的样本复用同理)。每个桶同时存结果本身的 pp,以便返回 PMF。
  • 用途:PowerLightSampler 按功率选光源(见 多光源采样与光源 BVH)。

水塘采样(A.2)

  • 基本算法(源自磁带时代,只能顺序读一遍、数据放不进内存):第 nn 个候选以概率 1/n1/n 放入水塘。归纳证明:前 nn 个之后水塘中每个候选的概率都是 1/n1/n;第 n+1n+1 个以 1/(n+1)1/(n+1) 进入,原样本以 n/(n+1)n/(n+1) 保留,乘积仍是 1/(n+1)1/(n+1)。Vitter 1985 把这个基本算法归功于 Alan Waterman。
  • 加权版本(Chao 1982):候选以 w/∑ww/\sum w 的概率替换,WeightedReservoirSampler 记录权重和与水塘样本的权重,PMF 即二者之比。另有一个 Add() 接受回调,只在样本真正入选时才计算其值,适合"权重便宜、值昂贵"的情况。 加权水塘每次看到新候选,就以「自身权重 / 已见权重和」的概率替换水塘中的样本。
flowchart TD
  Cand["新候选权重 w"] --> Sum["累加 W ← W + w"]
  Sum --> Prob["替换概率 = w / W"]
  Prob --> Roll{"掷骰接受?"}
  Roll -->|"是"| Rep["水塘 ← 该候选"]
  Roll -->|"否"| Keep["水塘不变"]
  • 合并:以"第二个水塘的权重和 / 两者权重和"的概率取第二个水塘的样本,结果与一个水塘看过全部候选完全等价——这正是 Add() 的操作(练习 A.1 要求证明)。这一性质使得空间、时间复用成为可能,如 ReSTIR(Bitterli 等 2020,PBRT 第 16 章与 多光源采样与光源 BVH 的延伸阅读都提到它)。水塘采样本身是一种重要性重采样,其无偏性来自 蒙特卡洛积分与重要性采样 中的离散估计。
  • 数组中的采样器用默认构造,之后用 Seed() 分别设置随机数种子(见 伪随机数生成)。

其他加速表格化分布查找的方法(延伸阅读)

引导表 (guide table):Chen–Asau 1974,Cline 等 2009 引入图形学并提出分段线性逆 CDF 的常数时间近似;Lawrence 等 2005 的自适应 CDF;GPU 上并行构建的基数树森林(Binder–Keller 2020);用光追硬件做逆变换采样(Morrical–Zellmann 2021);从算术编码角度看,最优的二叉查找树就是 Huffman 树(MacKay 2003、Piponi 2012)。别名法最早由 Walker(1974、1977)提出,Schwarz 2011 有详细的实现讨论。

取舍

方法预处理每次采样内存适用
线性扫描 SampleDiscrete()无O(n)O(n)无额外结果少、只采一次
CDF + 二分O(n)O(n)O(log⁡n)O(\log n)n+1n+1需要单调映射(保持低差异样本的分层)
别名表O(n)O(n)O(1)O(1)2n2n大量采样;但映射不单调,相邻 uu 可能映到相距很远的结果,会破坏低差异样本的结构
水塘采样无每个候选 O(1)O(1),单次遍历O(1)O(1)候选数未知或很大、流式生成、需要合并
  1. PBRT 4e source excerpt: chapter A (Sampling Algorithms)
  2. Pharr, Jakob, Humphreys《Physically Based Rendering: From Theory to Implementation》第 4 版(MIT Press, 2023)附录 A A.1–A.2 节与延伸阅读,pbr-book.org/4ed/Sampling_Algorithms

相关页面

游戏数学随机变量变换与逆变换采样Random Variable Transforms & Inversion Sampling蒙特卡洛估计器要求能从指定的概率密度里抽样。最基本的工具是逆变换法 (Inversion Method):对 CDF 求逆,把 [0,1) 上的均匀随机数映射成目标分布;更一般地,随机变量经过一一映射后的密度由导数(多维时是雅可比行列式)决定;多维非独立分布则先采边缘密度、再采条件密度。本页按 PBRT 第 4 版 2.1.1、2.3、2.4 节整理,是 蒙特卡洛积分与重要性采样 的数学部分。图形学多光源采样与光源 BVHMany-Light Sampling & Light BVH场景有成百上千个光源时,不可能在每个着色点对所有光源做直接光照。利用求和的蒙特卡洛估计 \sumi fi\approx fj/p(j),每次只随机选一个(或几个)光源并除以其概率即可无偏。选择概率可以均匀、按功率,或随着色点变化——后者需要一棵把光源聚合起来的层次结构(光源 BVH)。本页对应 PBRT 4e 12.6 节,并对照实时渲染的分簇/分块光源剔除。图形学蒙特卡洛积分与重要性采样Monte Carlo Integration & Importance Sampling渲染方程里的半球积分没有闭式解,只能数值积分。蒙特卡洛积分在定义域里随机取样、按概率密度加权求平均,期望值正好等于积分真值,误差随样本数 N 按 1/\sqrt{N} 下降,而且与积分维数无关,所以成了光线追踪类渲染的数学基础。降低误差的主要手段是让采样分布接近被积函数的形状,也就是重要性采样(GEA4 卷 II 12.6.2、12.6.4)。游戏数学伪随机数生成Pseudorandom Number Generation游戏引擎到处要用随机数,但随机数生成器 (RNG) 并不真正随机:它只是给出一个复杂但完全确定、预先定义的序列,因此严格说应叫伪随机数生成器 (PRNG)。好坏取决于周期(多久重复)和能否通过各种公认的随机性测试。GEA 介绍了线性同余、梅森旋转、Marsaglia 的 Mother-of-All / Xorshift / KISS99,以及 PCG。

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原