在实现粒子系统时,希望粒子能均匀分布在某个范围内,很容易想到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。
1unsigned long bit_reverse(unsigned long n)
2{
3 n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa);
4 n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc);
5 n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0);
6 n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00);
7 n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
8 return n;
9}
对于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;
}