Какой самый правильный способ генерировать случайные числа в C с помощью pthread - PullRequest
8 голосов
/ 17 октября 2011

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

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

Ответы [ 6 ]

8 голосов
/ 17 октября 2011

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

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

4 голосов
/ 18 октября 2011

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

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

1 голос
/ 01 июля 2015

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

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

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

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

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.

1 голос
/ 17 октября 2011

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

0 голосов
/ 14 апреля 2016

В системах 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)

0 голосов
/ 17 октября 2011

Эта ссылка предоставляет некоторый код для чтения из / dev / random - который должен быть потокобезопасным - и вопрос такой же:

Чтение / dev / urandom поточно-ориентировано?

...