Cách tạo một số nguyên ngẫu nhiên từ trong một phạm vi


108

Đây là phần tiếp theo từ một câu hỏi đã đăng trước đây:

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

Tôi muốn có thể tạo một số ngẫu nhiên từ trong một phạm vi cụ thể, chẳng hạn như 1 đến 6 để bắt chước các mặt của một con xúc sắc.

Tôi sẽ đi đâu để tới đó?


3
nếu bạn nhìn vào câu trả lời thứ hai cho câu hỏi bạn đề cập đến, bạn sẽ có câu trả lời. rand ()% 6.
Mats Fredriksson

2
Tôi không hiểu nó hoạt động như thế nào, vì vậy tôi quyết định đặt một câu hỏi riêng cho rõ ràng.
Jamie Keeling

2
Suy nghĩ ngẫu nhiên: Nếu bạn thăm dò ý kiến ​​của một nhóm lập trình viên ngẫu nhiên, bạn sẽ thấy một số ngẫu nhiên trong số họ đang nghĩ cách ngẫu nhiên để tạo ra các số. Xem xét Vũ trụ được điều hành bởi các quy luật chính xác và có thể dự đoán được, không phải là thú vị khi chúng ta cố gắng tạo ra mọi thứ một cách ngẫu nhiên hơn? Những câu hỏi như thế này luôn có xu hướng mang lại hơn 10k áp phích.
Armstrongest

2
@Mats rand ()% 6 có thể trả về 0. Không tốt cho việc chết.
new123456

Bạn có thể đánh dấu stackoverflow.com/a/6852396/419 là câu trả lời được chấp nhận thay vì câu trả lời liên kết đến nó không :) Cảm ơn.
Kev

Câu trả lời:


173

Tất cả các câu trả lời cho đến nay đều sai về mặt toán học. Việc trả về rand() % Nkhông đồng nhất đưa ra một số trong phạm vi [0, N)trừ khi Nchia độ dài của khoảng mà rand()trả về (tức là lũy thừa của 2). Hơn nữa, người ta không biết liệu các modul rand()có độc lập hay không: có thể chúng đi 0, 1, 2, ..., điều này đồng nhất nhưng không phải ngẫu nhiên. Giả thiết duy nhất có vẻ hợp lý là rand()đưa ra phân phối Poisson: bất kỳ hai khoảng con không trùng lặp nào có cùng kích thước đều có khả năng xảy ra như nhau và độc lập. Đối với một tập hợp các giá trị hữu hạn, điều này ngụ ý một phân phối đồng đều và cũng đảm bảo rằng các giá trị của rand()được phân tán độc đáo.

Điều này có nghĩa là cách đúng duy nhất để thay đổi phạm vi của rand()là chia nó thành các hộp; ví dụ: nếu RAND_MAX == 11và bạn muốn một phạm vi 1..6, bạn nên gán {0,1}cho 1,{2,3} cho 2, v.v. Đây là những khoảng rời rạc, có kích thước bằng nhau và do đó được phân bố đồng đều và độc lập.

Đề xuất sử dụng phép chia dấu phẩy động là hợp lý về mặt toán học nhưng có vấn đề về làm tròn về nguyên tắc. Có lẽdouble là độ chính xác đủ cao để làm cho nó hoạt động; có lẽ không. Tôi không biết và tôi không muốn phải tìm ra nó; trong mọi trường hợp, câu trả lời là phụ thuộc vào hệ thống.

Cách đúng là sử dụng số học số nguyên. Đó là, bạn muốn một cái gì đó như sau:

#include <stdlib.h> // For random(), RAND_MAX

// Assumes 0 <= max <= RAND_MAX
// Returns in the closed interval [0, max]
long random_at_most(long max) {
  unsigned long
    // max <= RAND_MAX < ULONG_MAX, so this is okay.
    num_bins = (unsigned long) max + 1,
    num_rand = (unsigned long) RAND_MAX + 1,
    bin_size = num_rand / num_bins,
    defect   = num_rand % num_bins;

  long x;
  do {
   x = random();
  }
  // This is carefully written not to overflow
  while (num_rand - defect <= (unsigned long)x);

  // Truncated division is intentional
  return x/bin_size;
}

Vòng lặp là cần thiết để có được một phân phối hoàn toàn đồng đều. Ví dụ, nếu bạn được cung cấp các số ngẫu nhiên từ 0 đến 2 và bạn chỉ muốn các số từ 0 đến 1, bạn cứ tiếp tục kéo cho đến khi không nhận được số 2; không khó để kiểm tra xem điều này cho kết quả 0 hoặc 1 với xác suất bằng nhau. Phương pháp này cũng được mô tả trong liên kết mà không có trong câu trả lời của họ, mặc dù được mã hóa khác nhau. Tôi đang sử dụng random()hơn rand()là vì nó có phân phối tốt hơn (như trang người đàn ông đã lưu ý chorand() ).

Nếu bạn muốn nhận các giá trị ngẫu nhiên nằm ngoài phạm vi mặc định [0, RAND_MAX], thì bạn phải làm điều gì đó khó khăn. Có lẽ thích hợp nhất là để xác định một chức năng random_extended()mà kéo nbit (sử dụng random_at_most()) và lợi nhuận trong [0, 2**n), và sau đó áp dụng random_at_most()với random_extended()ở vị trí của random()(và 2**n - 1thay RAND_MAX) để kéo một giá trị ngẫu nhiên ít hơn 2**n, giả sử bạn có một kiểu số có thể chứa ví dụ một giá trị. Cuối cùng, tất nhiên, bạn có thể nhận được các giá trị đang [min, max]sử dụng min + random_at_most(max - min), bao gồm cả các giá trị âm.


1
@Adam Rosenfield, @ Ryan Reich: Trong một câu hỏi liên quan mà Adam đã trả lời: stackoverflow.com/questions/137783/… , câu trả lời được ủng hộ nhiều nhất: Việc sử dụng 'modulus' sau đó sẽ không chính xác, phải không? Để tạo 1..7 từ 1..21, nên sử dụng quy trình mà Ryan đã mô tả. Vui lòng sửa cho tôi nếu tôi sai.
Arvind

1
Khi xem xét thêm, một vấn đề khác ở đây là điều này sẽ không hoạt động khi max - min > RAND_MAX, điều này nghiêm trọng hơn vấn đề tôi đã nêu ở trên (ví dụ: VC ++ RAND_MAXchỉ có 32767).
13

2
Vòng lặp while có thể dễ đọc hơn. Thay vì thực hiện phép gán trong điều kiện, bạn có thể muốn a do {} while().
theJPster,

4
Hey, câu trả lời này được trích dẫn bởi Comet OS cuốn sách;) Lần đầu tiên tôi thấy rằng trong một cuốn sách dạy
vpuente

3
Nó cũng được trích dẫn trong cuốn sách OSTEP :) pages.cs.wisc.edu/~remzi/OSTEP (Chương 9, Trang 4)
rafascar

33

Tiếp theo câu trả lời của @Ryan Reich, tôi nghĩ tôi sẽ cung cấp phiên bản đã được làm sạch của mình. Kiểm tra giới hạn đầu tiên không bắt buộc với kiểm tra giới hạn thứ hai và tôi đã thực hiện nó lặp đi lặp lại thay vì đệ quy. Nó trả về các giá trị trong phạm vi [min, max], trong đó max >= min1+max-min < RAND_MAX.

unsigned int rand_interval(unsigned int min, unsigned int max)
{
    int r;
    const unsigned int range = 1 + max - min;
    const unsigned int buckets = RAND_MAX / range;
    const unsigned int limit = buckets * range;

    /* Create equal size buckets all in a row, then fire randomly towards
     * the buckets until you land in one of them. All buckets are equally
     * likely. If you land off the end of the line of buckets, try again. */
    do
    {
        r = rand();
    } while (r >= limit);

    return min + (r / buckets);
}

28
Lưu ý rằng điều này sẽ bị mắc kẹt trong một vòng lặp vô hạn nếu phạm vi> = RAND_MAX. Hỏi tôi làm thế nào tôi biết: /
theJPster

24
Làm sao bạn biết!?
Fantastic Mr Fox,

1
Lưu ý rằng bạn đang so sánh một int với một int không dấu (r> = limit). Vấn đề được giải quyết dễ dàng bằng cách tạo limitmột int (và tùy chọn bucket) kể từ RAND_MAX / range< INT_MAXbuckets * range<= RAND_MAX. CHỈNH SỬA: Tôi đã gửi và chỉnh sửa đề xuất.
rrrrrrrrrrrrrrrr

giải pháp từ @Ryan Reich vẫn mang lại cho tôi sự phân phối tốt hơn (ít thiên vị hơn)
Vladimir

20

Đây là công thức nếu bạn biết giá trị tối đa và giá trị nhỏ nhất của một dải ô và bạn muốn tạo các số bao gồm giữa dải ô:

r = (rand() % (max + 1 - min)) + min

9
Như đã lưu ý trong câu trả lời của Ryan, điều này tạo ra một kết quả thiên lệch.
David Wolever

6
Kết quả sai lệch, tiềm năng inttràn với max+1-min.
Chux - Khôi phục Monica

1
điều này chỉ hoạt động với số nguyên tối thiểu và tối đa. Nếu min và max là float, nó không có khả năng để thực hiện hoạt động%
Taioli Francesco

17
unsigned int
randr(unsigned int min, unsigned int max)
{
       double scaled = (double)rand()/RAND_MAX;

       return (max - min +1)*scaled + min;
}

Xem ở đây để biết các tùy chọn khác.


2
@ S.Lott - không hẳn vậy. Mỗi loại phân phối các trường hợp tỷ lệ cược cao hơn một chút khác nhau, vậy thôi. Phép toán kép tạo ấn tượng rằng có độ chính xác cao hơn ở đó, nhưng bạn có thể dễ dàng sử dụng (((max-min+1)*rand())/RAND_MAX)+minvà có thể có được cùng một phân phối chính xác (giả sử rằng RAND_MAX đủ nhỏ so với int để không bị tràn).
Steve314,

4
Điều này hơi nguy hiểm: điều này có thể (rất hiếm) trả về max + 1, nếu một trong hai rand() == RAND_MAX, hoặc rand()rất gần RAND_MAXvà lỗi dấu phẩy động đẩy kết quả cuối cùng về phía trước max + 1. Để an toàn, bạn nên kiểm tra xem kết quả có nằm trong phạm vi không trước khi trả lại.
Mark Dickinson

1
@Christoph: Tôi đồng ý về RAND_MAX + 1.0. Tuy nhiên, tôi vẫn không chắc điều đó đủ tốt để ngăn chặn việc max + 1trả lại: cụ thể là + minở phần cuối liên quan đến một vòng có thể kết thúc tạo ra max + 1các giá trị lớn của rand (). An toàn hơn nên từ bỏ hoàn toàn cách tiếp cận này và sử dụng số học nguyên.
Mark Dickinson

3
Nếu RAND_MAXđược thay thế bằng RAND_MAX+1.0như Christoph gợi ý, sau đó tôi tin rằng đây là an toàn với điều kiện + minđược thực hiện sử dụng số nguyên số học: return (unsigned int)((max - min + 1) * scaled) + min. Lý do (không rõ ràng) là giả sử IEEE 754 là số học và từ nửa tròn đến chẵn, (và điều đó cũng có max - min + 1thể biểu diễn chính xác dưới dạng nhân đôi, nhưng điều đó sẽ đúng trên một máy thông thường), điều đó luôn đúng x * scaled < xvới bất kỳ đôi nào tích cực xvà bất kỳ đôi nào scaledthỏa mãn 0.0 <= scaled && scaled < 1.0.
Mark Dickinson

1
Không cho randr(0, UINT_MAX): luôn tạo ra 0.
Chux - Khôi phục Monica

12

Bạn sẽ không chỉ làm:

srand(time(NULL));
int r = ( rand() % 6 ) + 1;

%là toán tử mô đun. Về cơ bản, nó sẽ chỉ chia cho 6 và trả lại phần còn lại ... từ 0 - 5


1
Nó sẽ cho kết quả từ 1 - 6. Đó là ý nghĩa của +1.
Armstrongest

4
Simon, hãy cho tôi xem một libc đang được sử dụng ở bất kỳ đâu rand()bao gồm các bit bậc thấp của trạng thái của bộ tạo (nếu nó sử dụng LCG). Tôi chưa thấy cái nào cho đến nay — tất cả chúng (có, bao gồm cả MSVC với RAND_MAX chỉ là 32767) loại bỏ các bit bậc thấp. Sử dụng mô-đun không được khuyến khích vì các lý do khác, cụ thể là nó làm lệch phân phối có lợi cho các số nhỏ hơn.
Joey

@Johannes: Vì vậy, có an toàn khi nói rằng máy đánh bạc không sử dụng mô-đun?
Armstrongest

Làm cách nào để loại trừ số 0? Có vẻ như nếu tôi chạy nó trong một vòng lặp 30, có thể lần thứ hai hoặc thứ ba nó chạy sẽ có số 0 khoảng một nửa. Đây có phải là một số loại sán?
Jamie Keeling

@Johannes: Có thể ngày nay nó không phải là vấn đề quá lớn, nhưng sử dụng các bit bậc thấp theo cách truyền thống là không nên. c-faq.com/lib/randrange.html
jamesdlin

9

Đối với những người hiểu vấn đề thiên vị nhưng không thể chịu được thời gian chạy không thể đoán trước của các phương pháp dựa trên từ chối, chuỗi này tạo ra một số nguyên ngẫu nhiên ít thiên vị dần dần trong [0, n-1] khoảng thời gian:

r = n / 2;
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
r = (rand() * n + r) / (RAND_MAX + 1);
...

Nó làm như vậy bằng cách tổng hợp một số ngẫu nhiên điểm cố định có độ chính xác cao i * log_2(RAND_MAX + 1) bit (ở đó ilà số lần lặp) và thực hiện phép nhân dài vớin .

Khi số lượng bit đủ lớn so với n , độ lệch trở nên nhỏ vô cùng.

Không quan trọng nếu RAND_MAX + 1nhỏ hơn n(như trong câu hỏi này ), hoặc nếu nó không phải là lũy thừa của hai, nhưng phải cẩn thận để tránh tràn số nguyên nếu RAND_MAX * nlớn hơn.


2
RAND_MAXthường xuyên INT_MAX, vì vậy RAND_MAX + 1-> UB (như INT_MIN)
chux - Phục hồi Monica

@chux đó là những gì tôi muốn nói về "phải cẩn thận để tránh tràn số nguyên nếu RAND_MAX * nlớn". Bạn cần sắp xếp để sử dụng các loại phù hợp với yêu cầu của mình.
sh1

@chux " RAND_MAXthường là INT_MAX" Có, nhưng chỉ trên hệ thống 16 bit! Bất kỳ công cụ lưu trữ hiện đại hợp lý nào sẽ đặt INT_MAXở 2 ^ 32/2 và RAND_MAXở 2 ^ 16 / 2. Đây có phải là một giả định không chính xác?
cat

2
@cat Hôm nay đã kiểm tra 2 inttrình biên dịch 32-bit , tôi đã tìm thấy RAND_MAX == 32767trên cái này và RAND_MAX == 2147483647trên cái khác. Kinh nghiệm tổng thể của tôi (nhiều thập kỷ) là điều đó RAND_MAX == INT_MAXthường xuyên hơn. Vì vậy, không đồng ý rằng một kiến ​​trúc 32-bit hiện đại hợp lý chắc chắn sẽ có một RAND_MAXlúc 2^16 / 2. Kể từ khi thông số kỹ thuật C cho phép 32767 <= RAND_MAX <= INT_MAX, tôi viết mã cho điều đó hơn là một xu hướng.
chux - Phục hồi Monica

3
Vẫn được bao phủ bởi "phải cẩn thận để tránh tràn số nguyên".
sh1

4

Để tránh sai lệch mô-đun (được đề xuất trong các câu trả lời khác), bạn luôn có thể sử dụng:

arc4random_uniform(MAX-MIN)+MIN

Trong đó "MAX" là giới hạn trên và "MIN" là giới hạn dưới. Ví dụ: đối với các số từ 10 đến 20:

arc4random_uniform(20-10)+10

arc4random_uniform(10)+10

Giải pháp đơn giản và tốt hơn là sử dụng "rand ()% N".


1
Tuyệt vời, câu trả lời này tốt hơn gấp tỷ lần so với các câu trả lời khác. Đáng lưu ý bạn cần phải làm #include <bsd/stdlib.h>trước. Ngoài ra, bất kỳ ý tưởng nào về cách tải điều này trên Windows mà không cần MinGW hoặc CygWin?
cat

1
Không, nó không tốt hơn các câu trả lời khác, bởi vì các câu trả lời khác chung chung hơn. Ở đây bạn bị giới hạn đối với arc4random, các câu trả lời khác cho phép bạn chọn một nguồn ngẫu nhiên khác, hoạt động với các loại số khác nhau, ... và cuối cùng nhưng không kém phần quan trọng chúng có thể giúp ai đó hiểu được vấn đề. Đừng quên rằng câu hỏi này cũng rất thú vị đối với những người khác có thể có một số yêu cầu đặc biệt hoặc không có quyền truy cập vào arc4random ... Tuy nhiên, nếu bạn có quyền truy cập vào nó và muốn có một giải pháp nhanh chóng, đó thực sự là một câu trả lời rất hay 😊
K. Biermann

4

Đây là một thuật toán đơn giản hơn một chút so với giải pháp của Ryan Reich:

/// Begin and end are *inclusive*; => [begin, end]
uint32_t getRandInterval(uint32_t begin, uint32_t end) {
    uint32_t range = (end - begin) + 1;
    uint32_t limit = ((uint64_t)RAND_MAX + 1) - (((uint64_t)RAND_MAX + 1) % range);

    /* Imagine range-sized buckets all in a row, then fire randomly towards
     * the buckets until you land in one of them. All buckets are equally
     * likely. If you land off the end of the line of buckets, try again. */
    uint32_t randVal = rand();
    while (randVal >= limit) randVal = rand();

    /// Return the position you hit in the bucket + begin as random number
    return (randVal % range) + begin;
}

Example (RAND_MAX := 16, begin := 2, end := 7)
    => range := 6  (1 + end - begin)
    => limit := 12 (RAND_MAX + 1) - ((RAND_MAX + 1) % range)

The limit is always a multiple of the range,
so we can split it into range-sized buckets:
    Possible-rand-output: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
    Buckets:             [0, 1, 2, 3, 4, 5][0, 1, 2, 3, 4, 5][X, X, X, X, X]
    Buckets + begin:     [2, 3, 4, 5, 6, 7][2, 3, 4, 5, 6, 7][X, X, X, X, X]

1st call to rand() => 13
     13 is not in the bucket-range anymore (>= limit), while-condition is true
         retry...
2nd call to rand() => 7
     7 is in the bucket-range (< limit), while-condition is false
         Get the corresponding bucket-value 1 (randVal % range) and add begin
    => 3

1
RAND_MAX + 1có thể dễ dàng intbổ sung tràn . Trong trường hợp đó, (RAND_MAX + 1) % rangesẽ tạo ra các kết quả đáng ngờ. Xem xét(RAND_MAX + (uint32_t)1)
Chux - Khôi phục Monica

2

Mặc dù Ryan đúng, nhưng giải pháp có thể đơn giản hơn nhiều dựa trên những gì đã biết về nguồn gốc của sự ngẫu nhiên. Để trình bày lại sự cố:

  • Có một nguồn ngẫu nhiên, xuất ra các số nguyên trong phạm vi [0, MAX) với phân bố đồng đều.
  • Mục đích là tạo ra các số nguyên ngẫu nhiên được phân phối đồng nhất trong phạm vi [rmin, rmax]nơi 0 <= rmin < rmax < MAX.

Theo kinh nghiệm của tôi, nếu số lượng thùng (hoặc "hộp") nhỏ hơn đáng kể so với phạm vi của các số ban đầu nguồn ban đầu mạnh về mặt mật mã - thì không cần phải trải qua tất cả các quy tắc nghiêm ngặt đó và phân chia mô-đun đơn giản sẽ đủ (như output = rnd.next() % (rmax+1), nếu rmin == 0), và tạo ra các số ngẫu nhiên được phân phối đồng nhất "đủ" và không làm giảm tốc độ. Yếu tố quan trọng là nguồn ngẫu nhiên (ví dụ, trẻ em, không nên thử điều này ở nhà vớirand() ).

Đây là một ví dụ / bằng chứng về cách nó hoạt động trong thực tế. Tôi muốn tạo các số ngẫu nhiên từ 1 đến 22, có một nguồn mật mã mạnh tạo ra các byte ngẫu nhiên (dựa trên Intel RDRAND). Kết quả là:

Rnd distribution test (22 boxes, numbers of entries in each box):     
 1: 409443    4.55%
 2: 408736    4.54%
 3: 408557    4.54%
 4: 409125    4.55%
 5: 408812    4.54%
 6: 409418    4.55%
 7: 408365    4.54%
 8: 407992    4.53%
 9: 409262    4.55%
10: 408112    4.53%
11: 409995    4.56%
12: 409810    4.55%
13: 409638    4.55%
14: 408905    4.54%
15: 408484    4.54%
16: 408211    4.54%
17: 409773    4.55%
18: 409597    4.55%
19: 409727    4.55%
20: 409062    4.55%
21: 409634    4.55%
22: 409342    4.55%   
total: 100.00%

Điều này gần giống với sự đồng nhất mà tôi cần cho mục đích của mình (ném xúc xắc công bằng, tạo ra các cuốn sách mã mạnh về mặt mật mã cho các máy mật mã Thế chiến II chẳng hạn như http://users.telenet.be/d.rijmenant/en/kl-7sim.htm , v.v. ). Đầu ra không cho thấy bất kỳ sự thiên vị đáng kể nào.

Đây là nguồn của trình tạo số ngẫu nhiên mạnh (đúng) về mặt mật mã: Trình tạo số ngẫu nhiên kỹ thuật số Intel và mã mẫu tạo ra số ngẫu nhiên 64 bit (không dấu).

int rdrand64_step(unsigned long long int *therand)
{
  unsigned long long int foo;
  int cf_error_status;

  asm("rdrand %%rax; \
        mov $1,%%edx; \
        cmovae %%rax,%%rdx; \
        mov %%edx,%1; \
        mov %%rax, %0;":"=r"(foo),"=r"(cf_error_status)::"%rax","%rdx");
        *therand = foo;
  return cf_error_status;
}

Tôi đã biên dịch nó trên Mac OS X với clang-6.0.1 (thẳng) và với gcc-4.8.3 sử dụng cờ "-Wa, q" (vì GAS không hỗ trợ các hướng dẫn mới này).


Được biên dịch với gcc randu.c -o randu -Wa,q(GCC 5.3.1 trên Ubuntu 16) hoặc clang randu.c -o randu(Clang 3.8.0) hoạt động, nhưng kết xuất lõi trong thời gian chạy với Illegal instruction (core dumped). Bất kỳ ý tưởng?
con mèo

Đầu tiên, tôi không biết liệu CPU của bạn có thực sự hỗ trợ lệnh RDRAND hay không. Hệ điều hành của bạn khá gần đây, nhưng CPU có thể không. Thứ hai (nhưng điều này ít xảy ra hơn) - Tôi không biết Ubuntu bao gồm loại trình hợp ngữ nào (và Ubuntu có xu hướng khá ngược với các gói cập nhật wrt). Kiểm tra trang web của Intel mà tôi đã tham khảo để biết các cách kiểm tra xem CPU của bạn có hỗ trợ RDRAND hay không.
Chuột

Bạn thực sự có điểm tốt. Những gì tôi vẫn không thể nhận được là những gì quá sai với rand(). Tôi đã thử một số bài kiểm tra và đăng câu hỏi này nhưng tôi chưa thể tìm thấy câu trả lời chính xác.
myradio

1

Như đã nói trước đó, modulo không đủ vì nó làm lệch phân phối. Đây là mã của tôi che đi các bit và sử dụng chúng để đảm bảo phân phối không bị lệch.

static uint32_t randomInRange(uint32_t a,uint32_t b) {
    uint32_t v;
    uint32_t range;
    uint32_t upper;
    uint32_t lower;
    uint32_t mask;

    if(a == b) {
        return a;
    }

    if(a > b) {
        upper = a;
        lower = b;
    } else {
        upper = b;
        lower = a; 
    }

    range = upper - lower;

    mask = 0;
    //XXX calculate range with log and mask? nah, too lazy :).
    while(1) {
        if(mask >= range) {
            break;
        }
        mask = (mask << 1) | 1;
    }


    while(1) {
        v = rand() & mask;
        if(v <= range) {
            return lower + v;
        }
    }

}

Đoạn mã đơn giản sau đây cho phép bạn xem bản phân phối:

int main() {

    unsigned long long int i;


    unsigned int n = 10;
    unsigned int numbers[n];


    for (i = 0; i < n; i++) {
        numbers[i] = 0;
    }

    for (i = 0 ; i < 10000000 ; i++){
        uint32_t rand = random_in_range(0,n - 1);
        if(rand >= n){
            printf("bug: rand out of range %u\n",(unsigned int)rand);
            return 1;
        }
        numbers[rand] += 1;
    }

    for(i = 0; i < n; i++) {
        printf("%u: %u\n",i,numbers[i]);
    }

}

Trở nên khá kém hiệu quả khi bạn từ chối các số từ rand (). Điều này sẽ đặc biệt không hiệu quả khi phạm vi có kích thước có thể được viết là 2 ^ k + 1. Sau đó, gần một nửa số lần thử của bạn từ lệnh gọi rand () chậm sẽ bị từ chối bởi điều kiện. Có thể tốt hơn nếu chọn dải mô-đun RAND_MAX. Giống như: v = rand(); if (v > RAND_MAX - (RAND_MAX % range) -> reject and try again; else return v % range;Tôi hiểu rằng modulo hoạt động chậm hơn nhiều so với việc tạo mặt nạ, nhưng tôi vẫn nghĩ ..... nó nên được thử nghiệm.
Øystein Schønning-Johansen 17/10/13

rand()trả về một inttrong phạm vi [0..RAND_MAX]. Phạm vi đó có thể dễ dàng là một phạm vi con của uint32_tvà sau đó randomInRange(0, ,b)không bao giờ tạo ra các giá trị trong phạm vi (INT_MAX...b].
chux - Phục hồi Monica

0

Sẽ trả về một số dấu phẩy động trong phạm vi [0,1]:

#define rand01() (((double)random())/((double)(RAND_MAX)))
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.