Как генерировать равномерно распределенные случайные числа между 0 и 1 в коде C с использованием OpenMP? - PullRequest
1 голос
/ 19 февраля 2020

Я пытаюсь написать код OpenMP, в котором каждый поток будет работать с большими массивами равномерно распределенных случайных чисел от 0 до 1. Каждый поток должен иметь разные и независимые распределения случайных чисел. Кроме того, распределения случайных чисел должны отличаться при каждом вызове кода. Это то, что я использую прямо сейчас. Всегда ли это гарантирует, что каждый поток имеет свои собственные / разные последовательности случайных чисел? Будут ли последовательности отличаться при каждом вызове кода? Как правильно это сделать? В следующем коде каждый поток генерирует 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;
}

Ответы [ 3 ]

1 голос
/ 19 февраля 2020

Вы не упоминаете, какую ОС вы используете, но если это Linux или POSIX-совместимая система, есть erand48() для поточно-ориентированной генерации случайных чисел, равномерно распределенных в диапазоне [0.0, 1.0). Он использует 48-битное начальное число, которое передается в качестве аргумента. Генерация начального семени может быть сделано несколькими способами. OpenBSD и Linux имеют getentropy(), BSD имеют arc4random_buf(), вы можете прочитать из специального файла /dev/urandom во многих ОС или сделать что-то, как вы в настоящее время используя со временем, pid, et c. Я бы рекомендовал таймер с более высоким разрешением, чем time(), хотя - 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;
}
1 голос
/ 19 февраля 2020

В идеале вы должны использовать генератор случайных чисел, который предназначен для параллельного использования, где вы можете гарантировать, что каждый поток выбирает различную часть последовательности случайных чисел. (Предоставление произвольных, но разных начальных чисел большинству генераторов не гарантирует этого, поскольку они не дают никаких гарантий относительно того, как выбор начальных значений влияет на то, в какой последовательности вы находитесь. Таким образом, вы могли бы просто быть смещены на единицу.)

Я предлагаю вам прочитать Параллельные случайные числа так же просто, как документ 1,2,3 , а затем использовать один из их алгоритмов, которые реализованы, например, в Intel MKL (который доступно бесплатно для всех).

( MKL поддерживает : -

  • Основанный на счетчике генератор псевдослучайных чисел Philox4x32-10 с периодом 2 ** 128 PHILOX4X32X10 [Salmon11].
  • ARS-5 генератор псевдослучайных чисел на основе счетчика с периодом 2 ** 128, который использует инструкции из набора AES-NI ARS5 [Salmon11].)

Используя это, вы можете тривиально гарантировать, что генератор каждого потока будет генерировать независимую последовательность, чтобы вы не пробовали одну и ту же серию в несколько потоков.

Intel MKL можно загрузить ред. https://software.intel.com/en-us/mkl/choose-download

0 голосов
/ 19 февраля 2020

Вы используете xor времени (целое число, в секундах) и идентификатор потока для заполнения генератора. Очевидно, что это не обязательно уникально для всех потоков и всех времен, так что это не очень хорошая идея. Вам было бы намного лучше вызывать srand с выводом std :: random_device , который на большинстве машин (т. Е. Всего, кроме крошечного встроенного процессора) будет приводить ваш псевдослучайный генератор к реальной энтропии.

Пример кода от 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';
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...