你能写个真正小于n的随机数吗?

一个小于n的随机数, 即所有的在[0, n) 之间的数出现的概率是相同的。

平时,我写一个小于n的随机数总是:r  = rand() % n; 这样产生的数,是个真正的随机数吗?

关键一点是c程序中能够产生的伪随机数的个数是有限个,因为系统中会这样一个常量:RAND_MAX ,也是c库能够产生伪随机数的最大数目。

如果我们用一个连续生成的随机数列,其值都%2, 会发现其值是0, 1相间的。

当n非常大时,所有伪随机数出现 在[0, n) 和 [n, RAND_MAX]连个区间中的分布式不均衡,将导致产生的随机数并不是真正的随机的。

在《Accelerated C++》一书中给出一个完美的解决方案:使用桶机制,将所有的随机数分成n 个桶, 每个桶的bucket_size = RAND_MAX / n;

然后 r = rand() / bucket_size;  看生成伪随机数落在了那个桶里,该桶的编号,就是我们要找的小于n的随机数。



扩展问题: 怎样获取小于n的k个不同的随机数?


程序代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

//非真正随机的随机数生成器[0, n)
int getRandomNum(int n);

//使用分成n个桶,实现真正的[0, n)中随机数
int getRealRandomNum(int n);

//获取k个不同的小于n的随机数
void getKRandomNums(int n, int a[], int k);

//判断数组a中是否存在整数k
int exists(int a[], int len, int k);

int main()
{
    int i;

    srand((unsigned)time(NULL));
    for(i = 1; i < 1000; ++i) {
        printf("%d : %d \n", getRandomNum(i), getRealRandomNum(i) );
    }

    printf("Hello world!\n");

    return 0;
}


//非真正随机的随机数生成器[0, n)
int getRandomNum(int n) {
   //init the seed of rand()
   //srand((unsigned)time(NULL)); // this should be before getRandomNum call.

   return rand() % n;
}

//使用分成n个桶,实现真正的[0, n)中随机数
int getRealRandomNum(int n) {
    assert(n > 0 && n <= RAND_MAX);

    int r;
    const int bucket_size  = RAND_MAX / n;

    do {
        r = rand() / bucket_size;
    }while(r >= n);

    return r;
}

//获取k个不同的随机数
void getKRandomNums(int n, int a[], int k) {
    assert(n > 0 && n <= RAND_MAX && k<=n);

    int count;
    
    int r;

    a[0] = getRealRandomNum(n);    
    for(count = 1; count < k; ) {
        r = getRealRandomNum(n);
        if(exists(a, count, r)) continue;
        a[count++] = r;
    }


}

int exists(int a[], int len, int k) {
    int i;

    for(i = 0; i < len; ++i) {
        if(a[i] == k) return 1;
    }

    return 0;

}


后记:对于扩展问题,可能会有更好的解决方案,有待思考!


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值