Каков наиболее правильный способ генерации случайных чисел в C с помощью pthread

У меня одновременно работает несколько потоков, и каждый из них должен генерировать случайные числа. Я хочу понять, есть ли шаблон, которому нужно следовать, чтобы понять, правильно ли инициализировать случайный генератор с помощью srand в основном потоке или каждый поток должен инициализировать свой собственный случайный генератор. Кажется, что rand / srand не предназначены для использования с потоками, и мне интересно, как я могу справиться с потоками и случайными числами вместе. Спасибо

РЕДАКТИРОВАТЬ: Мне нужны чистые случайные числа, но я также заинтересован в создании детерминированной последовательности для целей тестирования. Я использую Linux, но предпочитаю писать код как можно более переносимым.


person Raffo    schedule 17.10.2011    source источник
comment
Вы хотите, чтобы последовательность была детерминированной или нет? - stackoverflow.com/questions/6467585/   -  person Flexo    schedule 17.10.2011
comment
Если у вас нет особых требований, используйте rand_r.   -  person David Schwartz    schedule 17.10.2011
comment
rand () является потокобезопасным в Linux, но не требуется для posix, хотя posix предоставляет для этой цели rand_r. glibc использует внутренний мьютекс для своего rand () - что может вызвать конкуренцию, если ваши потоки генерируют много случайных чисел   -  person nos    schedule 17.10.2011


Ответы (5)


В Linux вы можете использовать rand_r () для посредственного генератора или drand48_r () для гораздо лучшего. Оба являются потокобезопасными заменами для rand() и drand48(), принимая один аргумент, состоящий из текущего состояния, вместо использования глобального состояния.

Что касается вашего вопроса об инициализации, оба приведенных выше генератора позволяют вам запускать в любой момент, который вы пожелаете, поэтому вы не обязаны запускать их перед созданием ваших потоков.

person Michael Goldshteyn    schedule 17.10.2011
comment
rand_r устарел с POSIX 2008. Я не знаю почему, но хотел бы. - person con-f-use; 28.03.2014

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

В системах POSIX (где вы, кажется, находитесь) есть семейство функций *rand48, где erand48, nrand48 и jrand48 принимают состояние генератора случайных чисел в качестве входных значений. Таким образом, вы можете легко иметь независимые состояния в каждом потоке. Те, которые вы можете инициализировать с помощью известного числа (например, номера вашего потока), и у вас будет воспроизводимая последовательность случайных чисел. Или вы инициализируете его чем-то непредсказуемым, например, текущим временем и числом, чтобы иметь последовательности, которые различаются для каждого выполнения.

person Jens Gustedt    schedule 17.10.2011

В Windows вы можете использовать rand_s (), которая является потокобезопасной. Если вы уже используете Boost, тогда boost :: random компетентен (хотя я понимаю, что это помечено как C, а не C ++).

person Alastair Maw    schedule 17.10.2011

rand_r является потокобезопасным, но также является реентерабельным.

Код ниже генерирует псевдослучайные числа uint128_t с использованием алгоритма xorshift.

Дополнительные свойства:

  • совместно реентерабельный
  • без блокировки
  • потокобезопасный
  • сверхбыстрый
  • засеянный из двух вариантов источников энтропии

uintx_types.h:

#ifndef UINTX_TYPES_H_INCLUDED
#define UINTX_TYPES_H_INCLUDED

#include <inttypes.h>
#include <ctype.h>

typedef __uint128_t     uint128_t;
typedef __uint64_t      uint64_t;

#define UINT128_C(hi, lo)   (((uint128_t)(hi) << 64) | (uint128_t)(lo))
#define UINT128_MIN         UINT128_C( 0x0000000000000000, 0x0000000000000000 )
#define UINT128_0           UINT128_MIN
#define UINT128_MAX         (~(UINT128_0) - 1) 

#endif // UINTX_TYPES_H_INCLUDED

lf.h:

#ifndef LF_H_INCLUDED
#define LF_H_INCLUDED

#define AAF(ADDR, VAL)          __sync_add_and_fetch((ADDR), (VAL))

#endif // LF_H_INCLUDED

rand.h:

#ifndef RAND_H_INCLUDED
#define RAND_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

#include "lf.h"
#include "uintx_types.h"


#define URANDOM     "/dev/random"   

void        srand_init(void);
uint128_t   rand_range_128(uint128_t min, uint128_t max);

#endif // RAND_H_INCLUDED

rand.c:

#include "rand.h"

uint64_t    r[2];

uint64_t xorshift64star(int index) 
{   
    uint64_t    x;

    x = r[index];
    x ^= x >> 12; // a
    x ^= x << 25; // b
    x ^= x >> 27; // c
    x = x * UINT64_C(2685821657736338717);
    return AAF(&r[index], x);
}

void srand_init(void)
{
    struct timespec ts;
    size_t          nbytes;
    ssize_t         bytes_read;
    int             fd;

    clock_gettime(CLOCK_REALTIME, &ts);
    r[0] = (uint64_t)(ts.tv_sec * 1.0e9 + ts.tv_nsec);
    xorshift64star(0);

    if ((fd = open(URANDOM, O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH)) == -1)
    {
        r[1] = r[0] + 1;
        xorshift64star(1);
    }
    else
    {
        nbytes = sizeof(r[1]);
        bytes_read = read(fd, &r[1], nbytes);
        if ((bytes_read == 0) || (r[1] == 0ull))
        {
            r[1] = r[0] + 1;
            xorshift64star(1);
        }
        close(fd);
    }
}

uint64_t rand_64(void)
{
    return xorshift64star(0);
}

uint128_t rand_128(void) 
{
    uint128_t       r;

    r = xorshift64star(0);
    r = (r << 64) | xorshift64star(1);
    return r;
}


uint128_t rand_range_128(uint128_t min, uint128_t max)
{
    return (rand_128() % (max+1-min))+min;
}

test.c:

#define KEYS 1000

int main(int argc, char **argv)
{
    int             i;
    uint128_t       key;

    srand_init();

    for(i = 0; i <= KEYS; i++)
    {
        key = rand_range_128(UINT128_MIN, UINT128_MAX);
        printf("%016"PRIx64"%016"PRIx64"\n", (uint64_t)(key >> 64), (uint64_t)key);

    }
    return 0;
}

Скомпилируйте с помощью gcc (4.9.2) под Linux.

person Dawid Szymański    schedule 01.07.2015
comment
Извините за некропостинг здесь, но я хотел спросить, могу ли я изменить srand_init (void) в srand_init (unsigned int * seed) и вместо clock_gettime (CLOCK_REALTIME, & ts); r [0] = (uint64_t) (ts.tv_sec * 1.0e9 + ts.tv_nsec); просто поместите r [0] = uint64_t (* seed), чтобы я всегда мог выбрать, как инициализировать свой rng. - person Francesco Di Lauro; 28.08.2017
comment
Кроме того, как мне инициализировать этот rng потокобезопасным способом? Я создаю свои потоки, а затем меняю семя и использую srand_init (семя) в каждом потоке? или я могу безопасно вызвать rand_range_128 (UINT128_MIN, UINT128_MAX) внутри моей параллельной области? - person Francesco Di Lauro; 28.08.2017

В системах Linux вы можете использовать такую ​​функцию, как:

size_t random_between_range( size_t min, size_t max ){
    unsigned short state[3];
    unsigned int seed = time(NULL) + (unsigned int) pthread_self();
    memcpy(state, &seed, sizeof(seed));
    return min +  nrand48(state) % (max - min );
}

Здесь я должен сказать, что я действительно не знаю, соответствуют ли числа, сгенерированные этой функцией, нормальному распределению, другими словами, является ли эта функция действительным ГСЧ в диапазоне (мин., Макс.), Но, по крайней мере, я помог мне написать простой тест, который требовал некоторых случайных чисел.

Как видите, функция использует идентификатор потока POSIX, чтобы переупорядочить случайное начальное число. При этом каждый поток имеет собственное случайное начальное число вместо использования глобального состояния в зависимости от time(NULL)

person Mike Mountrakis    schedule 14.04.2016