你知道吗?
随机的并不一定是均匀的。有些特定情况,我们希望随机生成的数据同时也是均匀分布的,那用普通的随机方法就很难实现了。这个时候我们该怎么办呢?
既均匀又随机
其实想要均匀分布并不难,我们就让数据整齐地分布就可以了。比如想在一个大正方形里均匀点上圆点,咱们就在正方形里打上均匀的网格,再在所有交叉点点上圆点,圆点分布一定是均匀的。
可难就难在,有些时候我们不希望这些点有规律。比如我们想模拟水磨石的效果设计壁纸,如果彩点整整齐齐地排列,就略显刻意了。又或者景观设计师在一大片空地上规划种植树木,为了营造出随意自然生长的样子,减少人造林的刻意感,一排排一列列整齐的树木明显就不符合要求了。再比如咱们之前讨论过的,俄罗斯方块是不是随机掉落的,如果这些形状按照某个顺序循环掉落,虽然所有形状都会很均匀地出现,但这个游戏的可玩性也大大降低了。
均匀且无规律分布的水磨石图案 (素材 via freepick.com)
太有规律了不行,那随机生成总可以了吧?这回确实分布没有规律了,位置有随机性了,没那么刻意了。可是,我们设计壁纸时不希望有大片的空白,规划树林也希望树木分布尽量均匀,不要有的地方稀疏有的地方又太密。俄罗斯方块形状的“旱灾”和“涝灾”,就是因为如果真的用“扔骰子”的方法生成下一个形状,就免不了会出现同一个形状长时间不出现或者总是出现同一种形状的情况。所以,这些类型的场景都不适合随机选取。
有没有什么方法能让生成的数据既均匀又随机呢?
准随机序列
当然有啦!这可难不到数学家,这样的算法还不止一种呢!这类随机序列叫作“准随机序列”(quasirandom sequences),指的就是分布均匀的随机数。
今天,咱们就来一起看其中的一种比较好理解的算法。就以在单位长度的正方形里均匀又随机地生成点为例吧。
如果想要生成一个点,需要生成横坐标和纵坐标的值。我们先来看横坐标。
首先写出0到1之间的中点,也就是1/2;然后列出0到1之间的四等分点,也就是1⁄4和3⁄4;然后列出0到1之间的八等分点,也就是1⁄8, 5⁄8, 3⁄8, 7⁄8……为了让得到的序列分布更加均匀,在列出同一层的多个等分点时,也是按照一左一右的顺序跳跃列举的。不断这样下去,我们就能得到一个低差异序列(low-discrepancy sequence)。这个序列叫作霍尔顿序列(Halton sequences)。这是以数学家约翰·亨利·霍尔顿(John Henry Halton)的名字命名的。利用霍尔顿序列就能生成一系列准随机数(quasi-random number)。这个序列里的数就可以作为随机点的横坐标。
1⁄2, 1⁄4, 3⁄4, 1⁄8, 5⁄8, 3⁄8, 7⁄8, 1⁄16, 9⁄16, 5⁄16, 13⁄16, 3⁄16, 11⁄16, 7⁄16, 15⁄16, ...
再来看纵坐标,其实原理也是一样的。这一次我们先列出三等分点,再列出九等分点,再列出二十七等分点,不断这样下去,也会得到一个不一样的霍尔顿序列。这个序列里的数就可以作为随机点的纵坐标。
1⁄3, 2⁄3, 1⁄9, 4⁄9, 7⁄9, 2⁄9, 5⁄9, 8⁄9, 1⁄27, 10⁄27, 19⁄27, 4⁄27, 13⁄27, 22¡27, 7⁄27, 16⁄27, 25⁄27, 2⁄27, 11⁄27, 20⁄27, 5⁄27, 14⁄27, 23⁄27, 8⁄27, 17⁄27, 26⁄27, ...
现在,我们把这两个序列生成的数字一对一对结合起来,就得到了一个个点的坐标。下图是我们在画布上画出霍尔顿序列生成的点和随机分布的点进行的对比。
左图:由紫到黄逐渐添加的100个点,按二维霍尔顿序列分布。
右图:由紫到黄逐渐添加的100个点,随机分布。
左图:由紫到黄逐渐添加的1000个点,按二维霍尔顿序列分布。
右图:由紫到黄逐渐添加的1000个点,随机分布。
怎么样?明显霍尔顿序列生成的点更加均匀,同时也看不出什么明显的规律,正好符合我们的要求。