使用getrandom在C中随机浮动

use*_*010 13 c random

我试图在0和1之间生成一个随机浮点数(无论它在[0,1]还是[0,1]上对我来说都不重要).关于这个问题在网上的每一个问题似乎都涉及rand()电话,种子time(NULL),但我想能够每秒多次调用我的程序并且每次都得到不同的随机数.这引导我进入Linux中的getrandom系统调用,它来自/ dev/urandom.我想出了这个:

#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdint.h>

int main() {
  uint32_t r = 0;
  for (int i = 0; i < 20; i++) {
    syscall(SYS_getrandom, &r, sizeof(uint32_t), 0);
    printf("%f\n", ((double)r)/UINT32_MAX);
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的问题只是我是否正确地做到了这一点.它似乎有用,但我担心我会误用某些东西,并且旁边没有使用getrandom()在线的例子.

chu*_*ica 8

OP有2个问题:

  1. 如何非常随机地启动序列.

  2. 如何double在[0 ... 1)范围内生成a .

通常的方法是采用非常随机的来源,/dev/urandom或者来自syscall()或甚seed = time() ^ process_id;至和种子的结果srand().然后rand()根据需要打电话.

下面包括一个快速转向的方法来生成均匀[0.0 to 1.0)(线性分布).但是像所有随机生成函数一样,真正好的函数基于广泛的研究.这个只是rand()根据DBL_MANT_DIG和调用几次RAND_MAX,

[编辑]原始double rand_01(void)有一个弱点,它只生成2 ^ 52个不同的doubles而不是2 ^ 53.它已被修改.替代方案:远远低于此double版本rand_01_ld(void).

#include <assert.h>
#include <float.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

double rand_01(void) {
  assert(FLT_RADIX == 2); // needed for DBL_MANT_DIG
  unsigned long long limit = (1ull << DBL_MANT_DIG) - 1;
  double r = 0.0;
  do {
    r += rand();
    // Assume RAND_MAX is a power-of-2 - 1
    r /= (RAND_MAX/2 + 1)*2.0;
    limit = limit / (RAND_MAX/2 + 1) / 2;
  } while (limit);

  // Use only DBL_MANT_DIG (53) bits of precision.
  if (r < 0.5) {
    volatile double sum = 0.5 + r;
    r = sum - 0.5;
  }
  return r;
}

int main(void) {
  FILE *istream = fopen("/dev/urandom", "rb");
  assert(istream);
  unsigned long seed = 0;
  for (unsigned i = 0; i < sizeof seed; i++) {
    seed *= (UCHAR_MAX + 1);
    int ch = fgetc(istream);
    assert(ch != EOF);
    seed += (unsigned) ch;
  }
  fclose(istream);
  srand(seed);

  for (int i=0; i<20; i++) {
    printf("%f\n", rand_01());
  }

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果想要扩展到更宽的FP,则无符号宽整数类​​型可能不足.下面是一个没有这种限制的便携式方法.

long double rand_01_ld(void) {
  // These should be calculated once rather than each function call
  // Leave that as a separate implementation problem
  // Assume RAND_MAX is power-of-2 - 1
  assert((RAND_MAX & (RAND_MAX + 1U)) == 0);
  double rand_max_p1 = (RAND_MAX/2 + 1)*2.0;
  unsigned BitsPerRand = (unsigned) round(log2(rand_max_p1));
  assert(FLT_RADIX != 10);
  unsigned BitsPerFP = (unsigned) round(log2(FLT_RADIX)*LDBL_MANT_DIG);

  long double r = 0.0;
  unsigned i;
  for (i = BitsPerFP; i >= BitsPerRand; i -= BitsPerRand) {
    r += rand();
    r /= rand_max_p1;
  }
  if (i) {
    r += rand() % (1 << i);
    r /= 1 << i;
  }
  return r;
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*ala 5

如果您需要生成双精度数,可以使用以下算法:

CPython 使用以下算法生成随机数(更改了函数名,typedef和返回值,但算法保持不变):

double get_random_double() {
    uint32_t a = get_random_uint32_t() >> 5;
    uint32_t b = get_random_uint32_t() >> 6;
    return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
}
Run Code Online (Sandbox Code Playgroud)

该算法的来源是Takuji Nishimura和Makoto Matsumoto的Mersenne Twister 19937随机数发生器.遗憾的是,源中提到的原始链接不再可供下载.

CPython中对此功能的评论注意到以下内容:

[this function]是原始代码中名为genrand_res53的函数; 在[0,1]上生成一个53位分辨率的随机数; 请注意 9007199254740992 == 2**53; 我假设他们拼写" /2**53"是在(可能是徒劳的)希望编译器将在编译时优化除法的过程中相互补充. 671088642**26.实际上,a包含27个随机位向左移位26,并b填充53位分子的低26位.

原始代码将Isaku Wada归功于此算法,2002/01/09


从该代码简化,如果你想创建一个float快速,你应该掩盖uint32_twith (1 << FLT_MANT_DIG) - 1和bit 除以(1 << FLT_MANT_DIG)得到适当的[0, 1)间隔:

#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdint.h>
#include <float.h>

int main() {
    uint32_t r = 0;
    float result;
    for (int i = 0; i < 20; i++) {
        syscall(SYS_getrandom, &r, sizeof(uint32_t), 0);
        result = (float)(r & ((1 << FLT_MANT_DIG) - 1)) / (1 << FLT_MANT_DIG);
        printf("%f\n", result);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

由于可以假设你的Linux有一个C99编译器,我们可以使用ldexpf而不是那个部门:

#include <math.h>

result = ldexpf(r & ((1 << FLT_MANT_DIG) - 1), -FLT_MANT_DIG);
Run Code Online (Sandbox Code Playgroud)

要获得关闭间隔[0, 1],您可以稍微降低效率

result = ldexpf(r % (1 << FLT_MANT_DIG), -FLT_MANT_DIG);
Run Code Online (Sandbox Code Playgroud)

为了快速生成大量高质量的随机数,我只需使用系统调用来获取足够的数据来为PRNG或CPRNG播种,然后从那里开始.