如何使用 OpenMP 在 C 代码中生成 0 和 1 之间均匀分布的随机数?
How to generate uniformly distributed random numbers between 0 and 1 in a C code using OpenMP?
我正在尝试编写一个 OpenMP 代码,其中每个线程将处理 0 到 1 之间均匀分布的随机数的大数组。每个线程都需要具有不同且独立的随机数分布。此外,每次调用代码时,随机数分布都需要不同。这就是我现在正在使用的。这是否总能保证每个线程都有其 own/different 随机数序列?每次调用代码时序列都会不同吗?这样做的正确方法是什么?下面的代码让每个线程生成 5 个样本,但在实际中 运行 它将是数百万个。
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <time.h>
int main(int argc, char* argv[])
{
int numthreads,i;
#pragma omp parallel private(i)
{
int id;
id=omp_get_thread_num();
if(id==0) numthreads = omp_get_num_threads();
printf("thread %d \n",id);
srand(time(0)^omp_get_thread_num());
for (i=0; i<5; i++)
{
printf("thread %d: %d %.6f \n",id,i,(double)rand()/(double)RAND_MAX);
}
}
return 0;
}
您正在使用时间异或(整数,以秒为单位)和线程 ID 来为生成器设定种子。显然不一定在所有线程和所有时间都是唯一的,所以不是一个好主意。使用 std::random_device 的输出调用 srand 会好得多,在大多数机器上(即除微小的嵌入式 CPU 之外的任何东西)都会为您的伪随机生成器播种真实的熵。
来自 https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution 的示例代码:
#include <random>
#include <iostream>
int main()
{
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_real_distribution<> dis(1.0, 2.0);
for (int n = 0; n < 10; ++n) {
// Use dis to transform the random unsigned int generated by gen into a
// double in [1, 2). Each call to dis(gen) generates a new random double
std::cout << dis(gen) << ' ';
}
std::cout << '\n';
}
你没有提到你使用的是什么 OS,但是如果它是 Linux 或 POSIX 兼容系统,那么 erand48()
for thread-safe generation of random numbers uniformly distributed in the range [0.0, 1.0)
. It uses a 48-bit seed that's passed as an argument. Generating the initial seed can be done in a number of ways. OpenBSD and Linux have getentropy()
, BSDs have arc4random_buf()
, you can read from the /dev/urandom
special file on many OSes, or do something like you're currently using with time, pid, etc. I'd suggest a higher resolution timer than time()
, though - clock_gettime()
是一个很好的选择来源。
一个例子:
#include <errno.h>
#include <fcntl.h>
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
#pragma omp parallel for
for (int i = 0; i < 4; i++) {
unsigned short xi[3]; // PRNG state variable
#if 0
// OpenBSD 5.6+/Linux kernel 3.17+ and glibc 2.25+
if (getentropy(xi, sizeof xi) < 0) {
perror("getentropy");
exit(EXIT_FAILURE);
}
#else
// Read from /dev/urandom
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
perror("open /dev/urandom");
exit(EXIT_FAILURE);
}
if (read(fd, xi, sizeof xi) != sizeof xi) {
perror("read");
exit(EXIT_FAILURE);
}
close(fd);
#endif
for (int n = 0; n < 4; n++) {
printf("Thread %d random number %f\n", omp_get_thread_num(), erand48(xi));
}
}
return 0;
}
理想情况下,您应该使用专为并行使用而设计的随机数生成器,这样您可以保证每个线程都对随机数序列的不同部分进行采样。 (向大多数生成器提供任意但不同的种子并不能保证这一点,因为它们不能保证种子的选择如何影响你在序列中的位置。因此你 可能 只是被抵消一个。)
我建议您阅读 Parallel Random Numbers as Easy as 1,2,3 论文,然后使用他们的一种算法,例如在 Intel MKL 中实现的算法(每个人都可以免费获得)。
(MKL supports :-
- Philox4x32-10 基于计数器的伪随机数生成器
2**128 PHILOX4X32X10[Salmon11].
期
- ARS-5 基于计数器的伪随机数生成器,周期为 2**128,使用 AES-NI 集的指令
ARS5 [鲑鱼 11]。
)
通过使用它,您可以轻松确保每个线程的生成器将生成一个独立的序列,这样您就不会在多个线程中对同一个序列进行采样。
英特尔 MKL 可以从 https://software.intel.com/en-us/mkl/choose-download
下载
我正在尝试编写一个 OpenMP 代码,其中每个线程将处理 0 到 1 之间均匀分布的随机数的大数组。每个线程都需要具有不同且独立的随机数分布。此外,每次调用代码时,随机数分布都需要不同。这就是我现在正在使用的。这是否总能保证每个线程都有其 own/different 随机数序列?每次调用代码时序列都会不同吗?这样做的正确方法是什么?下面的代码让每个线程生成 5 个样本,但在实际中 运行 它将是数百万个。
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <time.h>
int main(int argc, char* argv[])
{
int numthreads,i;
#pragma omp parallel private(i)
{
int id;
id=omp_get_thread_num();
if(id==0) numthreads = omp_get_num_threads();
printf("thread %d \n",id);
srand(time(0)^omp_get_thread_num());
for (i=0; i<5; i++)
{
printf("thread %d: %d %.6f \n",id,i,(double)rand()/(double)RAND_MAX);
}
}
return 0;
}
您正在使用时间异或(整数,以秒为单位)和线程 ID 来为生成器设定种子。显然不一定在所有线程和所有时间都是唯一的,所以不是一个好主意。使用 std::random_device 的输出调用 srand 会好得多,在大多数机器上(即除微小的嵌入式 CPU 之外的任何东西)都会为您的伪随机生成器播种真实的熵。
来自 https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution 的示例代码:
#include <random>
#include <iostream>
int main()
{
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_real_distribution<> dis(1.0, 2.0);
for (int n = 0; n < 10; ++n) {
// Use dis to transform the random unsigned int generated by gen into a
// double in [1, 2). Each call to dis(gen) generates a new random double
std::cout << dis(gen) << ' ';
}
std::cout << '\n';
}
你没有提到你使用的是什么 OS,但是如果它是 Linux 或 POSIX 兼容系统,那么 erand48()
for thread-safe generation of random numbers uniformly distributed in the range [0.0, 1.0)
. It uses a 48-bit seed that's passed as an argument. Generating the initial seed can be done in a number of ways. OpenBSD and Linux have getentropy()
, BSDs have arc4random_buf()
, you can read from the /dev/urandom
special file on many OSes, or do something like you're currently using with time, pid, etc. I'd suggest a higher resolution timer than time()
, though - clock_gettime()
是一个很好的选择来源。
一个例子:
#include <errno.h>
#include <fcntl.h>
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
#pragma omp parallel for
for (int i = 0; i < 4; i++) {
unsigned short xi[3]; // PRNG state variable
#if 0
// OpenBSD 5.6+/Linux kernel 3.17+ and glibc 2.25+
if (getentropy(xi, sizeof xi) < 0) {
perror("getentropy");
exit(EXIT_FAILURE);
}
#else
// Read from /dev/urandom
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
perror("open /dev/urandom");
exit(EXIT_FAILURE);
}
if (read(fd, xi, sizeof xi) != sizeof xi) {
perror("read");
exit(EXIT_FAILURE);
}
close(fd);
#endif
for (int n = 0; n < 4; n++) {
printf("Thread %d random number %f\n", omp_get_thread_num(), erand48(xi));
}
}
return 0;
}
理想情况下,您应该使用专为并行使用而设计的随机数生成器,这样您可以保证每个线程都对随机数序列的不同部分进行采样。 (向大多数生成器提供任意但不同的种子并不能保证这一点,因为它们不能保证种子的选择如何影响你在序列中的位置。因此你 可能 只是被抵消一个。)
我建议您阅读 Parallel Random Numbers as Easy as 1,2,3 论文,然后使用他们的一种算法,例如在 Intel MKL 中实现的算法(每个人都可以免费获得)。
(MKL supports :-
- Philox4x32-10 基于计数器的伪随机数生成器 2**128 PHILOX4X32X10[Salmon11]. 期
- ARS-5 基于计数器的伪随机数生成器,周期为 2**128,使用 AES-NI 集的指令 ARS5 [鲑鱼 11]。 )
通过使用它,您可以轻松确保每个线程的生成器将生成一个独立的序列,这样您就不会在多个线程中对同一个序列进行采样。
英特尔 MKL 可以从 https://software.intel.com/en-us/mkl/choose-download
下载