Làm thế nào để tạo một int ngẫu nhiên trong C?


Câu trả lời:


662

Lưu ý : Không sử dụng rand()để bảo mật. Nếu bạn cần một số bảo mật bằng mật mã, thay vào đó hãy xem câu trả lời này .

#include <time.h>
#include <stdlib.h>

srand(time(NULL));   // Initialization, should only be called once.
int r = rand();      // Returns a pseudo-random integer between 0 and RAND_MAX.

Chỉnh sửa : Trên Linux, bạn có thể thích sử dụng ngẫu nhiên và ngẫu nhiên .


194
+1 vì đơn giản, nhưng có lẽ nên nhấn mạnh rằng srand () chỉ nên được gọi một lần . Ngoài ra, trong một ứng dụng luồng, bạn có thể muốn đảm bảo rằng trạng thái của trình tạo được lưu trữ trên mỗi luồng và gieo hạt cho trình tạo một lần cho mỗi luồng.
RBerteig

41
@trusktr, nó phức tạp. Đây là một lý do: time()chỉ thay đổi một lần mỗi giây. Nếu bạn chọn từ time(), cho mỗi cuộc gọi đến rand(), thì bạn sẽ nhận được cùng một giá trị cho mỗi cuộc gọi trong một giây. Nhưng lý do lớn hơn là các thuộc tính rand()và chức năng như nó được biết đến tốt nhất cho trường hợp sử dụng khi chúng được gieo chính xác một lần mỗi lần chạy, và không phải trên mỗi cuộc gọi. Tùy thuộc vào "tính ngẫu nhiên" với các thuộc tính chưa được kiểm tra hoặc chưa được chứng minh dẫn đến rắc rối.
RBerteig

9
@trusktr đối với một trình tạo đồng quy tuyến tính đơn giản ( rand()thường là như vậy), việc gieo hạt rand()tốt nhất sẽ không có tác dụng gì cả, và tệ nhất là sẽ phá vỡ phẩm chất đã biết của máy phát. Đây là một chủ đề sâu sắc. Bắt đầu với việc đọc Knuth Vol 2 Chương 3 về các số ngẫu nhiên là phần giới thiệu tốt nhất về toán học và cạm bẫy.
RBerteig

21
Tránh cảnh báo trình biên dịch với một nhóm:srand((unsigned int)time(NULL));
GiovaMaster

9
Hãy nhớ rằng đây vẫn là một cách yếu để nhìn thấy PRNG. Mới năm ngoái, một loại virus mã hóa trên Linux đã mắc lỗi gieo hạt giống theo thời gian và điều này đã làm giảm đáng kể không gian tìm kiếm. Tất cả những gì bạn phải làm là có được một ý tưởng đúng đắn khi nhiễm trùng xảy ra và sau đó thử hạt giống từ khoảng thời gian đó. Lần cuối tôi nghe nói, nguồn ngẫu nhiên tốt nhất là / dev / urandom, được cho là, được gieo từ một nguồn hỗn hợp các nguồn hỗn loạn như nhiệt độ trên phần cứng. Tuy nhiên, nếu tất cả những gì bạn thực sự muốn là để chương trình của bạn hoạt động khác nhau trên mỗi lần chạy, thì giải pháp trên là ổn.
Jemenake 7/2/2016

238

Các rand()chức năng trong <stdlib.h>lợi nhuận một số nguyên giả ngẫu nhiên giữa 0 và RAND_MAX. Bạn có thể sử dụng srand(unsigned int seed)để đặt hạt giống.

Đó là cách phổ biến để sử dụng %toán tử kết hợp với rand()để có được một phạm vi khác (mặc dù hãy nhớ rằng điều này sẽ loại bỏ tính đồng nhất một chút). Ví dụ:

/* random int between 0 and 19 */
int r = rand() % 20;

Nếu bạn thực sự quan tâm đến tính đồng nhất, bạn có thể làm một cái gì đó như thế này:

/* Returns an integer in the range [0, n).
 *
 * Uses rand(), and so is affected-by/affects the same seed.
 */
int randint(int n) {
  if ((n - 1) == RAND_MAX) {
    return rand();
  } else {
    // Supporting larger values for n would requires an even more
    // elaborate implementation that combines multiple calls to rand()
    assert (n <= RAND_MAX)

    // Chop off all of the values that would cause skew...
    int end = RAND_MAX / n; // truncate skew
    assert (end > 0);
    end *= n;

    // ... and ignore results from rand() that fall above that limit.
    // (Worst case the loop condition should succeed 50% of the time,
    // so we can expect to bail out of this loop pretty quickly.)
    int r;
    while ((r = rand()) >= end);

    return r % n;
  }
}

18
Đó là một thực tế phổ biến ổn, nhưng không phải là một thực tế chính xác. Xem nàynày .
Lazer

35
@Lazer: Đó là lý do tại sao tôi nói "mặc dù hãy nhớ rằng điều này sẽ loại bỏ tính đồng nhất phần nào".
Laurence Gonsalves

3
@AbhimanyuAryan Đây %là toán tử mô đun. Nó cung cấp cho bạn phần còn lại của một phép chia số nguyên, vì vậy x % nsẽ luôn luôn cung cấp cho bạn một số từ 0n - 1(càng lâu càng xnđều dương). Nếu bạn vẫn thấy khó hiểu, hãy thử viết một chương trình có isố lượng từ 0 đến 100 và in ra i % ncho một số nlựa chọn của bạn nhỏ hơn 100.
Laurence Gonsalves

2
@necromancer Tôi đã đi trước và thêm một giải pháp hoàn toàn thống nhất.
Laurence Gonsalves

2
@Lazer liên kết thứ hai bạn đăng thực sự vẫn chưa hoàn toàn thống nhất. Đúc thành một đôi và trở lại không giúp đỡ. Liên kết đầu tiên bạn đăng có một giải pháp thống nhất hoàn hảo, mặc dù nó sẽ lặp lại rất nhiều cho các giới hạn nhỏ trên. Tôi đã thêm một giải pháp hoàn toàn thống nhất cho câu trả lời này không nên lặp lại nhiều như vậy ngay cả đối với các giới hạn nhỏ.
Laurence Gonsalves

53

Nếu bạn cần các ký tự hoặc số nguyên ngẫu nhiên an toàn:

Như đã đề cập trong cách tạo số ngẫu nhiên một cách an toàn bằng nhiều ngôn ngữ lập trình khác nhau , bạn sẽ muốn thực hiện một trong những điều sau đây:

Ví dụ:

#include "sodium.h"

int foo()
{
    char myString[32];
    uint32_t myInt;

    if (sodium_init() < 0) {
        /* panic! the library couldn't be initialized, it is not safe to use */
        return 1; 
    }


    /* myString will be an array of 32 random bytes, not null-terminated */        
    randombytes_buf(myString, 32);

    /* myInt will be a random number between 0 and 9 */
    myInt = randombytes_uniform(10);
}

randombytes_uniform() là mật mã an toàn và không thiên vị.


libsodium RNG có nên được khởi tạo trước khi gọi Randombytes_buf không?
dùng2199593

Chỉ cần gọi sodium_init()tại một số điểm. Đừng lo lắng về RNG, nó sử dụng kernel.
Scott Arciszewski

Lưu ý: Tôi đã phê duyệt bản chỉnh sửa gần đây sodium_init()mặc dù nó không nhất thiết là một phần trong ví dụ của tôi vì đó là một chi tiết quan trọng.
Scott Arciszewski

29

Hãy đi qua đây. Đầu tiên chúng ta sử dụng hàm srand () để tạo hạt ngẫu nhiên. Về cơ bản, máy tính có thể tạo các số ngẫu nhiên dựa trên số được đưa vào srand (). Nếu bạn đã cho cùng một giá trị hạt giống, thì các số ngẫu nhiên tương tự sẽ được tạo ra mỗi lần.

Do đó, chúng ta phải chọn hạt ngẫu nhiên với giá trị luôn thay đổi. Chúng tôi làm điều này bằng cách cung cấp cho nó giá trị của thời gian hiện tại với hàm time ().

Bây giờ, khi chúng ta gọi rand (), một số ngẫu nhiên mới sẽ được tạo ra mỗi lần.

#include <stdio.h>

int random_number(int min_num, int max_num);

int main(void)
{
    printf("Min : 1 Max : 40 %d\n", random_number(1,40));
    printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
    return 0;
}

int random_number(int min_num, int max_num)
{
    int result = 0, low_num = 0, hi_num = 0;

    if (min_num < max_num)
    {
        low_num = min_num;
        hi_num = max_num + 1; // include max_num in output
    } else {
        low_num = max_num + 1; // include max_num in output
        hi_num = min_num;
    }

    srand(time(NULL));
    result = (rand() % (hi_num - low_num)) + low_num;
    return result;
}

12
Mã đẹp, nhưng không phải là một ý tưởng tốt để gọi 'srand (thời gian (NULL));'. phương thức này tạo ra cùng một số khi được gọi trong một vòng lặp for.
RayOldProf

1
Các chỉnh sửa được đề xuất liên quan đến mã thường bị từ chối. Ai đó đã thực hiện một ở đây với nhận xét "thuật toán đã sai. Có thể tạo ra số lượng lớn hơn mức tối đa". Không tự mình đánh giá yêu cầu bồi thường.
Martin Smith

1
@Martin Smith Vấn đề: 1) nên else{ low_num=max_num; hi_num=min_num+1;2) thất bại khi hi_num - low_num > INT_MAX. 3) Bỏ qua các giá trị trong tình huống hiếm INT_MAX > hi_num - low_num > RAND_MAX.
chux - Tái lập Monica

1
Sắp xếp lại nó như thế này sẽ khiến hàm này tạo ra cùng một số nếu nó được gọi nhiều lần trong cùng một giây. Nếu bạn thực sự muốn gieo nó, thì chỉ gieo một lần mỗi giây.
Élektra

1
Nhỏ: hi_num = max_num + 1;thiếu bảo vệ chống tràn.
chux - Phục hồi lại

25

Nếu bạn cần số ngẫu nhiên giả chất lượng tốt hơn những gì stdlibcung cấp, hãy xem Mersenne Twister . Nó cũng nhanh hơn. Mẫu triển khai rất phong phú, ví dụ ở đây .


2
+1: Trông thật tuyệt nhưng tôi chỉ đang làm một trò chơi đoán. Nếu tôi sẽ sử dụng một trình tạo số ngẫu nhiên trong một ứng dụng kinh doanh thì tôi chắc chắn sẽ sử dụng nó.
Kredns

4
Đừng sử dụng Mersenne Twister, hãy sử dụng thứ gì đó tốt như xoroshiro128 + hoặc PCG. (Liên kết có liên quan.)
Veedrac

17

Hàm C tiêu chuẩn là rand(). Nó đủ tốt để xử lý thẻ cho trò chơi bài, nhưng thật tệ. Nhiều triển khai rand()chu kỳ thông qua một danh sách ngắn các số và các bit thấp có chu kỳ ngắn hơn. Cách mà một số chương trình gọi rand()là khủng khiếp và tính toán một hạt giống tốt để vượt qua srand()thật khó khăn.

Cách tốt nhất để tạo số ngẫu nhiên trong C là sử dụng thư viện của bên thứ ba như OpenSSL. Ví dụ,

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>

/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
    union {
        unsigned int i;
        unsigned char c[sizeof(unsigned int)];
    } u;

    do {
        if (!RAND_bytes(u.c, sizeof(u.c))) {
            fprintf(stderr, "Can't get random bytes!\n");
            exit(1);
        }
    } while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
    return u.i % limit;
}

/* Random double in [0.0, 1.0) */
double random_double() {
    union {
        uint64_t i;
        unsigned char c[sizeof(uint64_t)];
    } u;

    if (!RAND_bytes(u.c, sizeof(u.c))) {
        fprintf(stderr, "Can't get random bytes!\n");
        exit(1);
    }
    /* 53 bits / 2**53 */
    return (u.i >> 11) * (1.0/9007199254740992.0);
}

int main() {
    printf("Dice: %d\n", (int)(random_uint(6) + 1));
    printf("Double: %f\n", random_double());
    return 0;
}

Tại sao nhiều mã? Các ngôn ngữ khác như Java và Ruby có các hàm cho số nguyên hoặc số float ngẫu nhiên. OpenSSL chỉ cung cấp các byte ngẫu nhiên, vì vậy tôi cố gắng bắt chước cách Java hoặc Ruby biến đổi chúng thành số nguyên hoặc số float.

Đối với số nguyên, chúng tôi muốn tránh sai lệch modulo . Giả sử rằng chúng tôi đã nhận được một số số nguyên 4 chữ số ngẫu nhiên từ rand() % 10000, nhưng rand()chỉ có thể trả về 0 đến 32767 (giống như trong Microsoft Windows). Mỗi số từ 0 đến 2767 sẽ xuất hiện thường xuyên hơn mỗi số từ 2768 đến 9999. Để xóa sai lệch, chúng tôi có thể thử lại rand()trong khi giá trị dưới 2768, vì 30000 giá trị từ 2768 đến 32767 ánh xạ đồng nhất vào 10000 giá trị từ 0 đến 9999.

Đối với số float, chúng tôi muốn có 53 bit ngẫu nhiên, bởi vì doubleđộ chính xác giữ 53 bit (giả sử đó là gấp đôi của IEEE). Nếu chúng ta sử dụng nhiều hơn 53 bit, chúng ta sẽ có độ lệch làm tròn. Một số lập trình viên viết mã như thế rand() / (double)RAND_MAX, nhưng rand()có thể chỉ trả về 31 bit hoặc chỉ 15 bit trong Windows.

RAND_bytes()Hạt giống của OpenSSL , có lẽ bằng cách đọc /dev/urandomtrong Linux. Nếu chúng ta cần nhiều số ngẫu nhiên, sẽ quá chậm để đọc tất cả chúng /dev/urandom, bởi vì chúng phải được sao chép từ kernel. Sẽ nhanh hơn khi cho phép OpenSSL tạo ra nhiều số ngẫu nhiên hơn từ một hạt giống.

Thông tin thêm về các số ngẫu nhiên:

  • Perl's Perl_seed () là một ví dụ về cách tính hạt giống trong C cho srand(). Nó trộn các bit từ thời điểm hiện tại, ID tiến trình và một số con trỏ, nếu không thể đọc được /dev/urandom.
  • Arc4random_uniform () của OpenBSD giải thích về độ lệch modulo.
  • API Java cho java.util.Random mô tả các thuật toán để loại bỏ sự thiên vị khỏi các số nguyên ngẫu nhiên và đóng gói 53 bit vào các số float ngẫu nhiên.

Cảm ơn bạn cho câu trả lời mở rộng này. Lưu ý rằng trong số 24 câu trả lời hiện tại cho câu hỏi này, bạn là người duy nhất có thêm một cách giải thích để giải quyết float/ double, vì vậy tôi đã làm rõ câu hỏi để bám vào các intcon số để tránh làm cho nó quá rộng. Có những câu hỏi C khác liên quan cụ thể đếnfloat / doublegiá trị ngẫu nhiên, vì vậy bạn có thể muốn đăng lại nửa sau câu trả lời của mình cho các câu hỏi như stackoverflow.com/questions/13408990/
Lỗi

11

Nếu hệ thống của bạn hỗ trợ arc4randomhọ các chức năng, tôi sẽ khuyên bạn nên sử dụng các randchức năng thay vì chức năng tiêu chuẩn .

Các arc4randomgia đình bao gồm:

uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)

arc4random trả về một số nguyên không dấu 32 bit ngẫu nhiên.

arc4random_bufđặt nội dung ngẫu nhiên trong tham số của nó buf : void *. Số lượng nội dung được xác định bởi bytes : size_ttham số.

arc4random_uniformtrả về một số nguyên không dấu 32 bit ngẫu nhiên theo quy tắc : 0 <= arc4random_uniform(limit) < limit, trong đó giới hạn cũng là một số nguyên 32 bit không dấu.

arc4random_stirđọc dữ liệu từ /dev/urandomvà chuyển dữ liệu sang arc4random_addrandomngẫu nhiên thêm nhóm số ngẫu nhiên nội bộ của nó.

arc4random_addrandomđược sử dụng arc4random_stirđể điền vào nhóm số ngẫu nhiên nội bộ của nó theo dữ liệu được truyền cho nó.

Nếu bạn không có các chức năng này, nhưng bạn đang ở trên Unix, thì bạn có thể sử dụng mã này:

/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */

int urandom_fd = -2;

void urandom_init() {
  urandom_fd = open("/dev/urandom", O_RDONLY);

  if (urandom_fd == -1) {
    int errsv = urandom_fd;
    printf("Error opening [/dev/urandom]: %i\n", errsv);
    exit(1);
  }
}

unsigned long urandom() {
  unsigned long buf_impl;
  unsigned long *buf = &buf_impl;

  if (urandom_fd == -2) {
    urandom_init();
  }

  /* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
  read(urandom_fd, buf, sizeof(long));
  return buf_impl;
}

Các urandom_init chức năng mở /dev/urandomthiết bị, và đặt các bộ mô tả tập tin trong urandom_fd.

Các urandom chức năng cơ bản là giống như một cuộc gọi đến rand, ngoại trừ an toàn hơn, và nó trả về một long(dễ dàng thay đổi).

Tuy nhiên, /dev/urandomcó thể hơi chậm, vì vậy bạn nên sử dụng nó làm hạt giống cho một trình tạo số ngẫu nhiên khác nhau.

Nếu hệ thống của bạn không có một /dev/urandom, nhưng không có một /dev/randomhoặc tập tin tương tự, sau đó bạn chỉ có thể thay đổi đường dẫn truyền cho opentrong urandom_init. Các cuộc gọi và API được sử dụng trongurandom_initurandom(tôi tin) tuân thủ POSIX và như vậy, sẽ hoạt động trên hầu hết, nếu không phải tất cả các hệ thống tuân thủ POSIX.

Ghi chú: Đọc từ /dev/urandom sẽ KHÔNG chặn nếu không có đủ entropy, do đó, các giá trị được tạo trong các trường hợp như vậy có thể không an toàn về mặt mật mã. Nếu bạn lo lắng về điều đó, thì hãy sử dụng /dev/random, nó sẽ luôn chặn nếu không có đủ entropy.

Nếu bạn đang ở trên một hệ thống khác (tức là Windows), thì hãy sử dụng rand hoặc một số API không di động phụ thuộc vào nền tảng cụ thể của Windows.

Wrapper chức năng cho urandom, randhoặc arc4randomcuộc gọi:

#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}

8

STL không tồn tại cho C. Bạn phải gọi rand, hoặc tốt hơn , random. Chúng được khai báo trong tiêu đề thư viện tiêu chuẩn stdlib.h. randlà POSIX, randomlà một chức năng đặc tả BSD.

Sự khác biệt giữa randrandomrandomtrả về số ngẫu nhiên 32 bit có thể sử dụng nhiều hơn và randthường trả về số 16 bit. Các trang web BSD cho thấy các bit thấp hơn randcó chu kỳ và có thể dự đoán được, do đó randcó khả năng vô dụng đối với số lượng nhỏ.


3
@Neil - vì tất cả các câu trả lời cho đến nay đều đề cập đến STL, tôi nghi ngờ rằng câu hỏi đã được chỉnh sửa nhanh để xóa tham chiếu cần thiết.
Michael Burr

rand () không vô dụng đối với các số nhỏ - bạn có thể chia nhỏ chúng ra và chỉ sử dụng các bit cao ngẫu nhiên hơn nếu bạn thực sự cần.
Chris Lutz

@Chris, bạn có thể biết nếu kích thước của số ngẫu nhiên được biết, nhưng nếu kích thước yêu cầu của số ngẫu nhiên thay đổi trong thời gian chạy (chẳng hạn như xáo trộn một mảng động, v.v.) thì sẽ rất khó để làm việc xung quanh một cảnh báo như vậy.
dreamlax

Tôi không thể tìm thấy bất kỳ chức năng ngẫu nhiên nào ở đây :-(
kasia.b

@ kasia.b trong liên kết đó, có extern int rand(void);extern void srand(unsigned int);.
RastaJedi

7

Có một cái nhìn về ISAAC (Indirection, Shift, Accumulation, Add và Count). Nó phân bố đồng đều và có chiều dài chu kỳ trung bình là 2 ^ 8295.


2
ISAAC là một RNG thú vị vì tốc độ của nó nhưng chưa nhận được sự chú ý nghiêm trọng về mật mã.
user2398029

4

Đây là một cách tốt để có được một số ngẫu nhiên giữa hai số bạn chọn.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

    #define randnum(min, max) \
        ((rand() % (int)(((max) + 1) - (min))) + (min))

int main()
{
    srand(time(NULL));

    printf("%d\n", randnum(1, 70));
}

Đầu ra lần đầu tiên: 39

Đầu ra lần thứ hai: 61

Đầu ra lần thứ ba: 65

Bạn có thể thay đổi các giá trị sau randnumthành bất kỳ số nào bạn chọn và nó sẽ tạo ra một số ngẫu nhiên cho bạn giữa hai số đó.


4

Bạn muốn sử dụng rand(). Lưu ý ( RẤT QUAN TRỌNG ): đảm bảo đặt hạt giống cho chức năng rand. Nếu bạn không, số ngẫu nhiên của bạn không thực sự ngẫu nhiên . Điều này rất, rất, rất quan trọng. Rất may, bạn thường có thể sử dụng một số kết hợp của bộ đếm thời gian đánh dấu hệ thống và ngày để có được một hạt giống tốt.


6
Hai điểm a) số ngẫu nhiên của bạn không phải là "thực sự" ngẫu nhiên, bất kể bạn chọn máy phát điện như thế nào. Và b) rất thuận tiện khi có chuỗi giả ngẫu nhiên luôn giống nhau trong nhiều trường hợp - ví dụ để thử nghiệm.

16
nếu RẤT QUAN TRỌNG rằng số của bạn thực sự ngẫu nhiên, bạn không nên sử dụng hàm rand ().
tylerl

1
Các giá trị từ rand hoàn toàn không phải là "thực sự" ngẫu nhiên cho dù bạn có đặt hạt giống hay không. Cho một hạt giống được biết trình tự là có thể dự đoán. Tạo số ngẫu nhiên "thực sự" là khó khăn. Không có entropy liên quan đến rand.
dreamlax

2
Tất nhiên họ sẽ - thư viện tạo hạt giống cho bạn bởi thư viện (có thể bằng không, nhưng đó là hạt giống hợp lệ).

4
Ah, nhưng thuật toán đã biết / hạt giống đã biết là điều cần thiết để gỡ lỗi bất kỳ chương trình nào sử dụng số ngẫu nhiên. Không có gì lạ khi ghi lại hạt giống được sử dụng cùng với một lần chạy mô phỏng để nó có thể được tạo lại để phân tích chi tiết hơn. Không gọi srand () chút nào tương đương với gọi srand (1).
RBerteig

4

FWIW, câu trả lời là có, có một stdlib.hhàm được gọi rand; chức năng này được điều chỉnh chủ yếu cho tốc độ và phân phối, không phải là không thể đoán trước. Hầu như tất cả các hàm ngẫu nhiên tích hợp cho các ngôn ngữ và khung khác nhau đều sử dụng chức năng này theo mặc định. Ngoài ra còn có các trình tạo số ngẫu nhiên "mật mã" ít được dự đoán hơn, nhưng chạy chậm hơn nhiều. Chúng nên được sử dụng trong bất kỳ loại ứng dụng nào liên quan đến bảo mật.


4

Điều này hy vọng sẽ ngẫu nhiên hơn một chút so với việc chỉ sử dụng srand(time(NULL)).

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    srand((unsigned int)**main + (unsigned int)&argc + (unsigned int)time(NULL));
    srand(rand());

    for (int i = 0; i < 10; i++)
        printf("%d\n", rand());
}

1
thêm srand (rand ()) không làm tăng tính ngẫu nhiên của chuỗi nếu chương trình này được thực hiện nhiều lần trong vòng 1 giây. thời gian (NULL) vẫn sẽ trả về cùng một giá trị cho mỗi trong số chúng, rand () đầu tiên sẽ trả về cùng một thời gian và lệnh gọi thứ hai đến srand () sẽ có cùng giá trị, dẫn đến vẫn có cùng một chuỗi ngẫu nhiên. Việc sử dụng địa chỉ của argc có thể giúp ích, chỉ khi được đảm bảo rằng địa chỉ này sẽ khác nhau trên mỗi lần thực hiện chương trình, điều này không phải lúc nào cũng đúng.
theferrit32

3

Chà, STL là C ++, không phải C, vì vậy tôi không biết bạn muốn gì. Nếu bạn muốn C, tuy nhiên, có rand()và các srand()hàm:

int rand(void);

void srand(unsigned seed);

Cả hai đều là một phần của ANSI C. Ngoài ra còn có random()chức năng:

long random(void);

Nhưng theo như tôi có thể nói, random()không phải là ANSI tiêu chuẩn C. Thư viện của bên thứ ba có thể không phải là ý tưởng tồi, nhưng tất cả phụ thuộc vào mức độ ngẫu nhiên của một số bạn thực sự cần tạo.


3

Chương trình C để tạo số ngẫu nhiên từ 9 đến 50

#include <time.h>
#include <stdlib.h>

int main()
{
    srand(time(NULL));
    int lowerLimit = 10, upperLimit = 50;
    int r =  lowerLimit + rand() % (upperLimit - lowerLimit);
    printf("%d", r);
}

Nói chung, chúng ta có thể tạo một số ngẫu nhiên giữa lowLimit và UpperLimit-1

tức là lowLimit được bao gồm hoặc nói r ∈ [lowLimit, UpperLimit)


@Pang Đó là những gì tôi đã đề cập rõ ràng GIỮA 9 và 50 chứ không phải TỪ 9 và 50.
Shivam K. Thakkar

2
Hoạt động modulo của bạn giới thiệu một thiên vị.
jww

2

rand() là cách thuận tiện nhất để tạo số ngẫu nhiên.

Bạn cũng có thể bắt số ngẫu nhiên từ bất kỳ dịch vụ trực tuyến nào như Random.org.


1
Bạn cũng có thể bắt số ngẫu nhiên từ bất kỳ dịch vụ trực tuyến nào như Random.org Bounty nếu bạn bao gồm một cách di động, hiệu quả để thực hiện việc này trong C.
MD XF

2
#include <stdio.h>
#include <stdlib.h>

void main() 
{
    int visited[100];
    int randValue, a, b, vindex = 0;

    randValue = (rand() % 100) + 1;

    while (vindex < 100) {
        for (b = 0; b < vindex; b++) {
            if (visited[b] == randValue) {
                randValue = (rand() % 100) + 1;
                b = 0;
            }
        }

        visited[vindex++] = randValue;
    }

    for (a = 0; a < 100; a++)
        printf("%d ", visited[a]);
}

sau đó xác định ở đầu với các biến @ b4hand
Muhammad Sadiq

Vào thời điểm tôi đưa ra nhận xét đó, tôi không có quyền chỉnh sửa chung và nói chung các thay đổi mã đã thay đổi câu trả lời thực tế sẽ bị từ chối. Nếu bạn không quan tâm để sửa câu trả lời của mình, tôi có thể.
b4hand

Nếu ý định của bạn là tạo ra một hoán vị ngẫu nhiên của các giá trị duy nhất, mã này vẫn có lỗi. Nó tạo ra giá trị 84 hai lần và không tạo ra giá trị 48. Hơn nữa, nó không tạo ra trình tạo số ngẫu nhiên để trình tự giống nhau trên mỗi lần thực hiện.
b4hand

1
các biến a và b được định nghĩa trong mã này .. Không có lỗi .. cũng không có lỗi cú pháp và lỗi logic.
Muhammad Sadiq

Sai. Tôi đã tự tay xác nhận đầu ra như tôi đã đề cập.
b4hand

1
#include <stdio.h>
#include <dos.h>

int random(int range);

int main(void)
{
    printf("%d", random(10));
    return 0;
}

int random(int range)
{
    struct time t;
    int r;

    gettime(&t);
    r = t.ti_sec % range;
    return r;
}

1

Trên các CPU x86_64 hiện đại, bạn có thể sử dụng trình tạo số ngẫu nhiên phần cứng thông qua _rdrand64_step()

Mã ví dụ:

#include <immintrin.h>

uint64_t randVal;
if(!_rdrand64_step(&randVal)) {
  // Report an error here: random number generation has failed!
}
// If no error occured, randVal contains a random 64-bit number

1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

//generate number in range [min,max)
int random(int min, int max){
    int number = min + rand() % (max - min);
    return number; 
}

//Driver code
int main(){
    srand(time(NULL));
    for(int i = 1; i <= 10; i++){
        printf("%d\t", random(10, 100));
    }
    return 0;
}

0

Nghe một lời giải thích tốt về lý do tại sao sử dụng rand()để tạo ra các số ngẫu nhiên phân bố đồng đều trong một phạm vi nhất định là một ý tưởng tồi, tôi quyết định xem xét mức độ sai lệch của đầu ra thực sự. Trường hợp thử nghiệm của tôi là ném xúc xắc công bằng. Đây là mã C:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[])
{
    int i;
    int dice[6];

    for (i = 0; i < 6; i++) 
      dice[i] = 0;
    srand(time(NULL));

    const int TOTAL = 10000000;
    for (i = 0; i < TOTAL; i++)
      dice[(rand() % 6)] += 1;

    double pers = 0.0, tpers = 0.0;
    for (i = 0; i < 6; i++) {
      pers = (dice[i] * 100.0) / TOTAL;
      printf("\t%1d  %5.2f%%\n", dice[i], pers);
      tpers += pers;
    }
    printf("\ttotal:  %6.2f%%\n", tpers);
}

và đây là đầu ra của nó:

 $ gcc -o t3 t3.c
 $ ./t3 
        1666598  16.67%     
        1668630  16.69%
        1667682  16.68%
        1666049  16.66%
        1665948  16.66%
        1665093  16.65%
        total:  100.00%
 $ ./t3     
        1667634  16.68%
        1665914  16.66%
        1665542  16.66%
        1667828  16.68%
        1663649  16.64%
        1669433  16.69%
        total:  100.00%

Tôi không biết bạn cần số đồng phục ngẫu nhiên như thế nào, nhưng ở trên có vẻ đồng nhất đủ cho hầu hết các nhu cầu.

Chỉnh sửa: sẽ là một ý tưởng tốt để khởi tạo PRNG với một cái gì đó tốt hơn time(NULL).


rand () có thể thất bại trong các thử nghiệm ngẫu nhiên khác, chẳng hạn như các thử nghiệm cực đoan . rand () khác nhau từ nền tảng đến nền tảng; giá trị rand () từ GNU / Linux có thể tốt hơn giá trị từ BSD hoặc Windows.
George Koehler

Đây không phải là một cách hợp lệ để kiểm tra tính ngẫu nhiên.
Veedrac

Phụ thuộc vào mục đích và mô hình mối đe dọa / rủi ro. Đối với RNG mã hóa mạnh - chắc chắn, hãy sử dụng RDRAND (hoặc RDSEED). Đối với một người ném xúc xắc đơn giản (không phải cấp độ sòng bạc) IMHO ở trên là đủ. Từ khóa là " đủ tốt ".
Chuột

0

Tôi đã gặp một vấn đề nghiêm trọng với trình tạo số ngẫu nhiên giả trong ứng dụng gần đây của mình: Tôi đã gọi lại chương trình C của mình thông qua một tập lệnh pyhton và tôi đang sử dụng làm mã giống như sau:

srand(time(NULL))

Tuy nhiên, kể từ:

  • rand sẽ tạo ra chuỗi ngẫu nhiên giả giống nhau cho cùng một hạt giống trong srand (xem man srand );
  • Như đã nêu, chức năng thời gian chỉ thay đổi thứ hai từ giây: nếu ứng dụng của bạn được chạy nhiều lần trong cùng một giây, time sẽ trả về cùng một giá trị mỗi lần.

Chương trình của tôi tạo ra chuỗi số tương tự. Bạn có thể làm 3 điều để giải quyết vấn đề này:

  1. trộn thời gian đầu ra với một số thông tin khác thay đổi khi chạy (trong ứng dụng của tôi, tên đầu ra):

    srand(time(NULL) | getHashOfString(outputName))

    Tôi đã sử dụng djb2 làm hàm băm của mình.

  2. Tăng độ phân giải thời gian. Trên nền tảng của tôi, clock_gettimeđã có sẵn, vì vậy tôi sử dụng nó:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
  3. Sử dụng cả hai phương pháp với nhau:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec | getHashOfString(outputName));

Tùy chọn 3 đảm bảo cho bạn (theo như tôi biết) tính ngẫu nhiên hạt giống tốt nhất, nhưng nó có thể tạo ra sự khác biệt chỉ trên ứng dụng rất nhanh. Theo tôi, lựa chọn 2 là đặt cược an toàn.


Ngay cả với các heuristic này, đừng dựa vào rand () cho dữ liệu mật mã.
domenukk

rand()Tôi không nên sử dụng dữ liệu mật mã, tôi đồng ý. Ít nhất là đối với tôi, ứng dụng của tôi không liên quan đến dữ liệu mật mã, vì vậy đối với tôi, đó là phương pháp đã cho.
Koldar

0

Mặc dù tất cả những người đề nghị rand()ở đây, bạn không muốn sử dụng rand()trừ khi bạn phải làm thế! Các số ngẫu nhiên màrand() tạo ra thường rất xấu. Để trích dẫn từ trang người đàn ông Linux:

Các phiên bản rand()srand()trong Thư viện Linux C sử dụng cùng một trình tạo số ngẫu nhiên như random(3)srandom(3), do đó, các bit bậc thấp hơn sẽ ngẫu nhiên như các bit bậc cao hơn. Tuy nhiên, trên các triển khai rand () cũ hơn và trên các triển khai hiện tại trên các hệ thống khác nhau, các bit bậc thấp ít ngẫu nhiên hơn nhiều so với các bit bậc cao hơn . Không sử dụng chức năng này trong các ứng dụng dự định có thể mang theo khi cần sự ngẫu nhiên tốt. ( Sử dụng random(3)thay thế. )

Về tính di động, random()cũng được xác định theo tiêu chuẩn POSIX từ khá lâu. rand()đã cũ hơn, nó đã xuất hiện trong thông số POSIX.1 đầu tiên (IEEE Std 1003.1-1988), trong khirandom() lần đầu tiên xuất hiện trong POSIX.1-2001 (IEEE Std 1003.1-2001), nhưng tiêu chuẩn POSIX hiện tại đã là POSIX.1-2008 (IEEE Std 1003.1-2008), đã nhận được bản cập nhật chỉ một năm trước (Phiên bản IEEE Std 1003.1-2008, 2016). Vì vậy tôi sẽ xem xétrandom() để rất di động.

POSIX.1-2001 cũng đã giới thiệu lrand48()và các mrand48()chức năng, xem tại đây :

Họ các hàm này sẽ tạo ra các số giả ngẫu nhiên bằng thuật toán đồng quy tuyến tính và số học số nguyên 48 bit.

Và nguồn ngẫu nhiên giả khá tốt là arc4random()chức năng có sẵn trên nhiều hệ thống. Không phải là một phần của bất kỳ tiêu chuẩn chính thức nào, xuất hiện trong BSD vào khoảng năm 1997 nhưng bạn có thể tìm thấy nó trên các hệ thống như Linux và macOS / iOS.


random()không tồn tại trên Windows.
Bjorn Lindqvist

@ BjornLindqvist Windows cũng không có hệ thống POSIX; đó là hệ thống duy nhất trên thị trường không hỗ trợ ít nhất các API POSIX cơ bản (thậm chí các hệ thống bị khóa như iOS cũng hỗ trợ). Windows chỉ hỗ trợ rand()vì nó cũng được yêu cầu bởi tiêu chuẩn C. Đối với bất cứ điều gì khác, bạn cần một giải pháp đặc biệt chỉ dành cho Windows, giống như bình thường. #ifdef _WIN32là cụm từ bạn sẽ thấy thường xuyên nhất trong mã đa nền tảng muốn hỗ trợ Windows cũng như thường có một giải pháp hoạt động với tất cả các hệ thống và một giải pháp chỉ dành cho Windows.
Mecki

0

Đối với các ứng dụng Linux C:

Đây là mã được làm lại của tôi từ một câu trả lời ở trên tuân theo các thực hành mã C của tôi và trả về một bộ đệm ngẫu nhiên có kích thước bất kỳ (với mã trả về thích hợp, v.v.). Hãy chắc chắn gọi urandom_open()một lần khi bắt đầu chương trình của bạn.

int gUrandomFd = -1;

int urandom_open(void)
{
    if (gUrandomFd == -1) {
        gUrandomFd = open("/dev/urandom", O_RDONLY);
    }

    if (gUrandomFd == -1) {
        fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
                  errno, strerror(errno));
        return -1;
    } else {
        return 0;
    }
}


void urandom_close(void)
{
    close(gUrandomFd);
    gUrandomFd = -1;
}


//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
    int ret = 0; // Return value

    if (gUrandomFd == -1) {
        fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
        return -1;
    }

    ret = read(gUrandomFd, buf, size);

    if (ret != size) {
        fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
                 ret, size);
        return -1;
    } else {
        return 0;
    }
}

-2

Giải pháp tối giản của tôi sẽ làm việc cho các số ngẫu nhiên trong phạm vi [min, max). Sử dụng srand(time(NULL))trước khi gọi hàm.

int range_rand(int min_num, int max_num) {
    if (min_num >= max_num) {
        fprintf(stderr, "min_num is greater or equal than max_num!\n"); 
    }
    return min_num + (rand() % (max_num - min_num));
} 

-3

Hãy thử điều này, tôi kết hợp nó từ một số khái niệm đã được tham chiếu ở trên:

/*    
Uses the srand() function to seed the random number generator based on time value,
then returns an integer in the range 1 to max. Call this with random(n) where n is an integer, and you get an integer as a return value.
 */

int random(int max) {
    srand((unsigned) time(NULL));
    return (rand() % max) + 1;
}

16
Mã này không tốt. Gọi srand()mỗi khi bạn muốn gọi rand()là một ý tưởng khủng khiếp. Vì time()thường trả về một giá trị trong vài giây, gọi hàm này một cách nhanh chóng sẽ trả về cùng một giá trị "ngẫu nhiên".
Blastfurnace

3
Chức năng này sẽ bị nhầm lẫn với random()chức năng của Unix .
George Koehler
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.