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

在实现粒子系统时,希望粒子能均匀分布在某个范围内,很容易想到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。

ContractedBlock.gifExpandedBlockStart.gifCode
1None.gifunsigned long bit_reverse(unsigned long n)
2ExpandedBlockStart.gifContractedBlock.gifdot.gif
{
3InBlock.gif       n = ((n >> 1& 0x55555555| ((n << 1& 0xaaaaaaaa
);
4InBlock.gif       n = ((n >> 2& 0x33333333| ((n << 2& 0xcccccccc
);
5InBlock.gif       n = ((n >> 4& 0x0f0f0f0f| ((n << 4& 0xf0f0f0f0
);
6InBlock.gif       n = ((n >> 8& 0x00ff00ff| ((n << 8& 0xff00ff00
);
7InBlock.gif       n = ((n >> 16& 0x0000ffff| ((n << 16& 0xffff0000
); 
8InBlock.gif       return
 n;
9ExpandedBlockEnd.gif}


对于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;
}

转载于:https://www.cnblogs.com/Tue/archive/2008/08/26/1269937.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值