生成均匀“随机数”,一种生成均匀分布数字的简单方法

在实现粒子系统时,希望粒子能均匀分布在某个范围内,很容易想到C++标准的随机数发生器(实际是产生的是伪随机数,一般使用所谓的线型同余法),但随机数的“均匀分布”需要无限多个样本,才能表现出均匀分布的特征,在一段短的时间内,经常产生一些不均匀。(如果是均匀的,那么买彩票就能根据以前出现的号码推断以后出现的号码。)
 
如果用随机数生成平面内的粒子坐标,将会得到不太均匀的分布,这里经过一番思考,想出来这个办法:

1.如果需要的是一维空间的均匀分布的位置,那么希望是每2次产生的数字分别属于2个象限,在一个象限内,数字某个1/2象限,在1/2象限里,又轮流出现在1/4象限内,依此类推。
2.如果需要的是二维空间的均匀分布的位置,那么希望是每4次产生的数字分别属于4个象限,在一个象限内,得到的数字轮流属于是此象限的某个1/4,依此类推。

在n维空间内,空间可以逐级分成2^n个象限,
很容易想到,在一维空间里,用二进制数字来表示位置,如果是4位二进制数字,可以表示32个数字,我们可以把空间分割成32份,数字是这样变化的:
0000
0001
0010
0011
0100
0101
0110
0111
...
在最低位,数字在较小范围内是均匀分布的,第2位在4个数字的空间内均匀分布,第3位在8个数字内是均匀分布。。。
如果把生成的数字限制在0~15之间,那么这样连续的数字,每个都会出现一次,是绝对均匀分布的。但在一段时间内,数字分布是不均匀的。那么均匀分布是什么样的呢,应该这样,每次产生一个数字,数字落在左右两边的次数最多相差为1,也就是说,数字轮流出现在左右两边,如果用最低位表示左右,那么很显然能满足要求。
如果我们用数字直接表示位置,0~0111在左边,1000~1111在右边,既最高位决定左右,这样问题解决了,把一个逐渐增大的数字(0~15)的各位翻转,最高位变成最低位,次高位变成第2位。。。 那么这个数字可以均匀出现在左右,那么是否在任意范围内均匀呢?

我们把结果的范围0~15分成任意多份,可以看到,当产生了N个数字(按顺序取0~255,然后翻转)时,数字出现在任何两个部分的的次数相差不超过1。

Code


对于4位二进制数,输入0~15,可以得到这样的结果:
0
        8
    4
            12
  2
          10
      6
              14
 1
         9
     5
             13
   3
           11
       7
               15

这种方法产生的数字可以用在粒子系统发生器中,或许也可以用在其他需要均匀分布资源的地方。
如果需要得到二维空间内的随机位置,可以用除法和求余的方法获得水平和垂直坐标。
刚才提到粒子系统,在粒子系统中,有时候需要在球面上产生均匀分布的粒子,可以用到上面的方法,但是还不够,下次再把方法写下来。

附:线型同余法示例代码:
int f()
{  static int r;
   r = ( 25173*r+13849 ) % 65536;
   return r;
}

posted @ 2008-08-26 22:42  JohnsonFeng  阅读(5575)  评论(0编辑  收藏  举报