2011-10-17 19 views
7

Tôi có một số luồng chạy đồng thời và mỗi chuỗi phải tạo số ngẫu nhiên. Tôi muốn hiểu nếu có một mô hình để làm theo, để hiểu nếu nó là chính xác để khởi tạo các máy phát điện ngẫu nhiên với srand trong thread chính hoặc nếu mỗi thread phải khởi tạo máy phát điện ngẫu nhiên của riêng mình. Có vẻ như rand/srand đã không được thiết kế để được sử dụng với các chủ đề và tôi tự hỏi làm thế nào tôi có thể đối phó với các chủ đề và số ngẫu nhiên với nhau. Cảm ơnCách chính xác nhất để tạo các số ngẫu nhiên trong C với pthread

EDIT: Tôi cần số ngẫu nhiên thuần túy, nhưng tôi cũng quan tâm đến việc tạo chuỗi xác định cho mục đích thử nghiệm. Tôi đang trên Linux, nhưng tôi thích viết mã càng di động càng tốt.

+2

Bạn có muốn chuỗi xác định hay không? - http://stackoverflow.com/questions/6467585/deterministic-random-number-generator-tied-to-instance-thread-independent/6467623#6467623 – Flexo

+1

Nếu bạn không có yêu cầu đặc biệt, hãy sử dụng 'rand_r'. –

+1

rand() là luồng an toàn trên linux nhưng không bắt buộc phải do posix, mặc dù posix cung cấp rand_r cho mục đích đó. glibc sử dụng một mutex nội bộ cho rand() của nó - mà có thể gây ra tranh chấp nếu chủ đề của bạn tạo ra rất nhiều số ngẫu nhiên – nos

Trả lời

7

Trên Linux, bạn có thể sử dụng rand_r() cho máy phát điện tầm thường hoặc chức năng drand48_r() để có hiệu suất tốt hơn nhiều. Cả hai là thay thế an toàn luồng cho rand()drand48(), bằng cách lấy một đối số bao gồm trạng thái hiện tại, thay vì sử dụng trạng thái toàn cầu.

Đối với câu hỏi của bạn khi khởi tạo, cả hai trình tạo ở trên đều cho phép bạn gieo hạt tại bất kỳ thời điểm nào bạn muốn, vì vậy bạn không bị buộc phải gieo chúng trước khi sinh ra chủ đề của mình.

+1

rand_r là lỗi thời kể từ POSIX 2008. Tôi không biết tại sao, nhưng rất thích. –

0

Trên Windows, bạn có thể sử dụng chức năng rand_s(), là chủ đề an toàn. Nếu bạn đã sử dụng Boost, thì boost::random là có thẩm quyền (mặc dù tôi đánh giá cao việc này được gắn thẻ C chứ không phải C++).

4

Khi làm việc với chủ đề và làm ví dụ như mô phỏng hoặc vì vậy nó là rất quan trọng là bạn có các máy phát ngẫu nhiên độc lập. Thứ nhất, sự phụ thuộc giữa chúng thực sự có thể thiên vị kết quả của bạn và sau đó cơ chế kiểm soát truy cập đến trạng thái của trình tạo ngẫu nhiên sẽ rất có khả năng làm chậm việc thực thi.

Trên hệ thống POSIX (nơi bạn dường như) có gia đình *rand48 chức năng nơi erand48, nrand48jrand48 mất trạng thái của máy phát ngẫu nhiên như các giá trị đầu vào. Vì vậy, bạn có thể dễ dàng có các trạng thái độc lập trong mỗi luồng. Những người bạn có thể khởi tạo với một số đã biết (ví dụ: số lượng chủ đề của bạn) và bạn sẽ có một chuỗi số ngẫu nhiên có thể lặp lại. Hoặc bạn khởi tạo nó với một cái gì đó không thể dự đoán được như thời gian hiện tại & số để có chuỗi thay đổi cho mỗi lần thực hiện.

1

rand_r an toàn chỉ nhưng cũng là reentrant.

Mã bên dưới tạo số uint128_t pseuso-random bằng thuật toán xorshift.

thuộc tính khác:

  • chia sẻ reentrant
  • lock-free
  • thread-safe
  • cực nhanh
  • hạt từ hai nguồn biến thể của enthropy

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; 
} 

Biên dịch với gcc (4.9.2) trong Linux.

+0

Xin lỗi vì "necroposting" ở đây, nhưng tôi muốn hỏi nếu tôi có thể sửa đổi srand_init (void) trong srand_init (unsigned int * seed) và thay vì clock_gettime (CLOCK_REALTIME, &ts); r [0] = (uint64_t) (ts .tv_sec * 1.0e9 + ts.tv_nsec); chỉ cần đặt r [0] = uint64_t (* seed), vì vậy tôi luôn có thể chọn cách khởi tạo rng của mình –

+0

Hơn nữa, làm thế nào tôi nên khởi tạo rng này theo cách chủ đề an toàn? Tôi sinh ra chủ đề của tôi và sau đó thay đổi hạt giống và sử dụng srand_init (hạt giống) trong mỗi thread? Hoặc tôi một cách an toàn có thể gọi rand_range_128 (UINT128_MIN, UINT128_MAX) bên trong khu vực song song của tôi? –

0

Trên các hệ thống Linux, bạn có thể sử dụng một chức năng như:

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); 
} 

Ở đây tôi phải nói rằng tôi không thực sự biết nếu số được tạo ra bởi chức năng phù hợp này để phân phối chuẩn hay nói cách khác nếu chức năng này một RNG hợp lệ trong phạm vi (min, max) nhưng ít nhất đã làm việc cho tôi để viết một chuẩn mực đơn giản yêu cầu một số số ngẫu nhiên.

Như bạn thấy, hàm sử dụng id chuỗi POSIX để sắp xếp lại hạt giống ngẫu nhiên. Làm như vậy, mỗi luồng có hạt giống ngẫu nhiên riêng thay vì sử dụng trạng thái toàn cục tùy thuộc vào time(NULL)

Các vấn đề liên quan