Tạo các số ngẫu nhiên đồng nhất trên toàn bộ phạm vi


93

Tôi cần tạo các số ngẫu nhiên trong một khoảng thời gian xác định, [max; min].

Ngoài ra, các số ngẫu nhiên phải được phân bố đồng đều trong khoảng thời gian, không nằm ở một điểm cụ thể.

Currenly tôi đang tạo là:

for(int i=0; i<6; i++)
{
    DWORD random = rand()%(max-min+1) + min;
}

Từ các thử nghiệm của tôi, các số ngẫu nhiên chỉ được tạo ra xung quanh một điểm.

Example
min = 3604607;
max = 7654607;

Số ngẫu nhiên được tạo:

3631594
3609293
3630000
3628441
3636376
3621404

Từ các câu trả lời bên dưới: OK, RAND_MAX là 32767. Tôi đang sử dụng nền tảng C ++ Windows. Có phương pháp nào khác để tạo các số ngẫu nhiên có phân phối đều không?



1
Tôi không có ý tưởng rằng C ++ rand()là đồng nhất. Bạn đang sử dụng thư viện nào? cstdlib.hcủa rand()KHÔNG đồng nhất: cplusplus.com/reference/cstdlib/rand
Mike Warren

3
Không, rand () là thống nhất (ngoại trừ một số triển khai lỗi sớm). những gì không thống nhất là sử dụng toán tử mô-đun '%' để hạn chế phạm vi. Xem stackoverflow.com/questions/2999075/… để có giải pháp thích hợp hoặc nếu bạn có sẵn 'arc4random_uniform' thì bạn cũng có thể sử dụng trực tiếp.
John Meacham

@ Alien01: Bạn có cân nhắc việc thay đổi câu trả lời được chấp nhận thành câu trả lời bằng "Giày" ("Tại sao rand là một ý tưởng tồi", v.v.)? Câu trả lời của tôi thực sự đã lỗi thời và mỗi khi tôi nhận được sự ủng hộ cho nó, tôi cảm thấy như ai đó đang chạy nhầm lối đi.
peterchen

Sách trắng đẹp về ngẫu nhiên trong c ++ 11.
Pupsik

Câu trả lời:


153

Tại sao rand một ý tưởng tồi

Hầu hết các câu trả lời bạn nhận được ở đây đều sử dụng randhàm và toán tử mô đun. Phương pháp đó có thể không tạo ra các số một cách đồng nhất (nó phụ thuộc vào phạm vi và giá trị củaRAND_MAX ), do đó không được khuyến khích.

C ++ 11 và tạo trên một phạm vi

Với C ++ 11, nhiều tùy chọn khác đã tăng lên. Một trong số đó phù hợp với yêu cầu của bạn, để tạo ra một số ngẫu nhiên trong một dãy, khá độc đáo: std::uniform_int_distribution. Đây là một ví dụ:

const int range_from  = 0;
const int range_to    = 10;
std::random_device                  rand_dev;
std::mt19937                        generator(rand_dev());
std::uniform_int_distribution<int>  distr(range_from, range_to);

std::cout << distr(generator) << '\n';

đây là ví dụ đang chạy.

Máy phát ngẫu nhiên khác

Các <random>tiêu đề cung cấp vô số máy phát điện số ngẫu nhiên khác với loại khác nhau của các bản phân phối bao gồm Bernoulli, Poisson và bình thường.

Làm cách nào để xáo trộn một container?

Tiêu chuẩn cung cấp std::shuffle, có thể được sử dụng như sau:

std::vector<int> vec = {4, 8, 15, 16, 23, 42};

std::random_device random_dev;
std::mt19937       generator(random_dev());

std::shuffle(vec.begin(), vec.end(), generator);

Thuật toán sẽ sắp xếp lại thứ tự các phần tử một cách ngẫu nhiên, với độ phức tạp tuyến tính.

Boost.Random

Một giải pháp thay thế khác, trong trường hợp bạn không có quyền truy cập vào trình biên dịch C ++ 11 +, là sử dụng Boost.Random . Giao diện của nó rất giống với giao diện C ++ 11.


22
HÃY CHÚ Ý cho câu trả lời này, vì nó hiện đại hơn rất nhiều.
gsamaras

Đây là câu trả lời đúng. Cảm ơn! Tuy nhiên, tôi muốn xem mô tả chuyên sâu hơn về từng bước của mã đó. Ví dụ: một mt19937loại là gì?
Apollo

@Apollo Tài liệu nói "Mersenne Twister 32-bit của Matsumoto và Nishimura, 1998". Tôi giả sử đó là một thuật toán để tạo ra các số giả ngẫu nhiên.
Giày vào

@Shoe, đối với một phạm vi nhất định, nó tạo ra các số theo cùng một thứ tự 1 9 6 2 8 7 1 4 7 7,. Bạn có làm thế nào để ngẫu nhiên hóa điều này mỗi khi chúng tôi chạy chương trình?

1
@Richard Giải pháp thay thế là gì?
Giày vào

59

[sửa] Cảnh báo: Không sử dụng rand()để thống kê, mô phỏng, mật mã hoặc bất cứ thứ gì nghiêm trọng.

Nó đủ tốt để làm cho các con số trông ngẫu nhiên đối với một người bình thường một cách vội vàng, không hơn.

Xem câu trả lời của @ Jefffrey để có các lựa chọn tốt hơn hoặc câu trả lời này cho các số ngẫu nhiên an toàn bằng tiền điện tử.


Nói chung, các bit cao cho thấy sự phân phối tốt hơn các bit thấp, vì vậy cách được khuyến nghị để tạo các số ngẫu nhiên của một dải ô cho các mục đích đơn giản là:

((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

Lưu ý : đảm bảo RAND_MAX + 1 không bị tràn (cảm ơn Demi)!

Phép chia tạo ra một số ngẫu nhiên trong khoảng [0, 1); "kéo dài" điều này đến phạm vi cần thiết. Chỉ khi max-min + 1 gần bằng RAND_MAX, bạn mới cần hàm "BigRand ()" như được đăng bởi Mark Ransom.

Điều này cũng tránh một số vấn đề cắt do mô-đun, điều này có thể làm xấu con số của bạn hơn nữa.


Trình tạo số ngẫu nhiên tích hợp không đảm bảo có chất lượng cần thiết cho các mô phỏng thống kê. Đối với con người thì các con số "trông ngẫu nhiên" là được, nhưng đối với một ứng dụng nghiêm túc, bạn nên chọn thứ gì đó tốt hơn - hoặc ít nhất là kiểm tra các thuộc tính của nó (phân bố đồng đều thường tốt, nhưng các giá trị có xu hướng tương quan và chuỗi là xác định ). Knuth có một luận thuyết tuyệt vời (nếu khó đọc) về trình tạo số ngẫu nhiên và gần đây tôi thấy LFSR là tuyệt vời và dễ thực hiện, do các thuộc tính của nó đều phù hợp với bạn.


4
BigRand có thể cho kết quả tốt hơn ngay cả khi phạm vi mong muốn không vượt quá RAND_MAX. Hãy xem xét khi RAND_MAX là 32767 và bạn muốn có 32767 giá trị có thể - hai trong số 32768 số ngẫu nhiên đó (bao gồm cả số 0) sẽ ánh xạ đến cùng một đầu ra và sẽ có khả năng xảy ra cao gấp đôi so với những số khác. Hầu như không phải là một tài sản ngẫu nhiên lý tưởng!
Mark Ransom vào

7
(RAND_MAX + 1) là một ý tưởng tồi. Điều này có thể thay đổi và cung cấp cho bạn một giá trị âm. Tốt hơn nên làm điều gì đó như: ((gấp đôi) RAND_MAX) + 1.0
Demi

3
@peterchen: Tôi nghĩ bạn đã hiểu sai những gì demi đang nói. Ý của cô ấy là: ( rand() / ((double)RAND_MAX+1)) * (max-min+1) + min Chỉ cần di chuyển chuyển đổi thành nhân đôi và tránh sự cố.
Mooing Duck

3
Ngoài ra, điều này chỉ thay đổi phân phối từ 32767 giá trị dưới cùng trong phạm vi thành 32767 giá trị được phân phối đều trong phạm vi và các giá trị 4017233 còn lại sẽ không bao giờ được chọn bởi thuật toán này.
Mooing Duck

1
Câu trả lời đã cho là sai 1. Phương trình đúng là: ((double) rand () / (RAND_MAX + 1.0)) * (max-min) + min "max-min + 1" được sử dụng khi sử dụng% not * . Bạn sẽ thấy tại sao khi bạn thực hiện min = 0, max = 1. Có thể peterchen hoặc @ peter-mortensen sửa đổi nó.
davepc

17

Tôi muốn bổ sung các câu trả lời xuất sắc của Angry Shoe và peterchen bằng một bản tổng quan ngắn về tình hình nghệ thuật trong năm 2015:

Một số lựa chọn tốt

randutils

Các randutilsthư viện (trình bày) là một sự mới lạ thú vị, cung cấp một giao diện đơn giản và (tuyên bố) khả năng ngẫu nhiên mạnh mẽ. Nó có những nhược điểm là nó làm tăng thêm sự phụ thuộc vào dự án của bạn và, là mới, nó chưa được thử nghiệm rộng rãi. Dù sao, miễn phí (giấy phép MIT) và chỉ có tiêu đề, tôi nghĩ nó đáng để thử.

Mẫu tối thiểu: một cuộn khuôn

#include <iostream>
#include "randutils.hpp"
int main() {
    randutils::mt19937_rng rng;
    std::cout << rng.uniform(1,6) << "\n";
}

Ngay cả khi ai không quan tâm đến thư viện, trang web ( http://www.pcg-random.org/ ) cung cấp nhiều bài viết thú vị về chủ đề sinh số ngẫu nhiên nói chung và thư viện C ++ nói riêng.

Boost.Random

Boost.Random (tài liệu) là thư viện mà lấy cảm hứng C++11's <random>, mà cổ phiếu nhiều giao diện. Mặc dù về mặt lý thuyết cũng là một phần phụ thuộc bên ngoài, Boostnhưng hiện tại có trạng thái là thư viện "gần chuẩn" và Randommô-đun của nó có thể được coi là sự lựa chọn cổ điển cho việc tạo số ngẫu nhiên chất lượng tốt. Nó có hai ưu điểm đối với C++11giải pháp:

  • nó di động hơn, chỉ cần hỗ trợ trình biên dịch cho C ++ 03
  • random_devicesử dụng các phương pháp cụ thể của hệ thống để cung cấp hạt giống chất lượng tốt

Lỗ hổng nhỏ duy nhất là việc cung cấp mô-đun random_devicekhông chỉ có tiêu đề, người ta phải biên dịch và liên kết boost_random.

Mẫu tối thiểu: một cuộn khuôn

#include <iostream>
#include <boost/random.hpp>
#include <boost/nondet_random.hpp>

int main() {
    boost::random::random_device                  rand_dev;
    boost::random::mt19937                        generator(rand_dev());
    boost::random::uniform_int_distribution<>     distr(1, 6);

    std::cout << distr(generator) << '\n';
}

Mặc dù mẫu tối thiểu hoạt động tốt, nhưng các chương trình thực nên sử dụng một số cải tiến:

  • làm mt19937mộtthread_local : trình tạo khá đầy đặn (> 2 KB) và tốt hơn là không được phân bổ trên ngăn xếp
  • hạt giống mt19937với nhiều hơn một số nguyên: Mersenne Twister có trạng thái lớn và có thể tận dụng nhiều entropy hơn trong quá trình khởi tạo

Một số lựa chọn không quá tốt

Thư viện C ++ 11

Mặc dù là giải pháp thành ngữ nhất, nhưng <random>thư viện không cung cấp nhiều để đổi lấy sự phức tạp của giao diện ngay cả đối với các nhu cầu cơ bản. Lỗ hổng nằm ở chỗ std::random_device: Tiêu chuẩn không bắt buộc bất kỳ chất lượng tối thiểu nào cho đầu ra của nó (miễn là entropy()trả về 0) và, kể từ năm 2015, MinGW (không phải là trình biên dịch được sử dụng nhiều nhất, nhưng hầu như không phải là lựa chọn bí truyền) sẽ luôn in 4trên mẫu tối thiểu.

Mẫu tối thiểu: một cuộn khuôn

#include <iostream>
#include <random>
int main() {
    std::random_device                  rand_dev;
    std::mt19937                        generator(rand_dev());
    std::uniform_int_distribution<int>  distr(1, 6);

    std::cout << distr(generator) << '\n';
}

Nếu việc triển khai không bị lỗi, giải pháp này sẽ tương đương với giải pháp Boost và áp dụng các đề xuất tương tự.

Giải pháp của Godot

Mẫu tối thiểu: một cuộn khuôn

#include <iostream>
#include <random>

int main() {
    std::cout << std::randint(1,6);
}

Đây là một giải pháp đơn giản, hiệu quả và gọn gàng. Chỉ có khiếm khuyết, sẽ mất một khoảng thời gian để biên dịch - khoảng hai năm, cung cấp C ++ 17 được phát hành đúng hạn và randintchức năng thử nghiệm được chấp thuận thành Tiêu chuẩn mới. Có thể đến lúc đó những đảm bảo về chất lượng hạt giống sẽ được cải thiện.

Các tệ hơn-là-tốt hơn giải pháp

Mẫu tối thiểu: một cuộn khuôn

#include <cstdlib>
#include <ctime>
#include <iostream>

int main() {
    std::srand(std::time(nullptr));
    std::cout << (std::rand() % 6 + 1);
}

Giải pháp C cũ được coi là có hại và vì những lý do chính đáng (xem các câu trả lời khác tại đây hoặc phân tích chi tiết này ). Tuy nhiên, nó có những ưu điểm: đơn giản, dễ di chuyển, nhanh chóng và trung thực, theo nghĩa người ta biết rằng các số ngẫu nhiên mà người ta nhận được hầu như không phù hợp, và do đó người ta không muốn sử dụng chúng cho các mục đích nghiêm túc.

Giải pháp troll kế toán

Mẫu tối thiểu: một cuộn khuôn

#include <iostream>

int main() {
    std::cout << 9;   // http://dilbert.com/strip/2001-10-25
}

Mặc dù 9 là một kết quả hơi bất thường đối với một cuộn chết thông thường, nhưng người ta phải thán phục sự kết hợp tuyệt vời của những phẩm chất tốt trong giải pháp này, giải pháp này trở thành giải pháp nhanh nhất, đơn giản nhất, thân thiện với bộ nhớ cache nhất và di động nhất. Bằng cách thay thế 9 bằng 4, người ta sẽ có được một bộ tạo hoàn hảo cho bất kỳ loại Dungeon và Rồng nào chết, trong khi vẫn tránh được các giá trị chứa đầy biểu tượng 1, 2 và 3. Một lỗ hổng nhỏ duy nhất là, do tính khí tồi tệ của những kẻ thích tính toán của Dilbert, chương trình này thực sự tạo ra hành vi không xác định.


Các randutilsthư viện được gọi là PCG bây giờ.
tay10r

11

Nếu RAND_MAXlà 32767, bạn có thể tăng gấp đôi số bit một cách dễ dàng.

int BigRand()
{
    assert(INT_MAX/(RAND_MAX+1) > RAND_MAX);
    return rand() * (RAND_MAX+1) + rand();
}

Tôi không nghĩ rằng điều này hiệu quả. Các trình tạo số ngẫu nhiên giả thường mang tính xác định. Ví dụ, nếu randcuộc gọi đầu tiên trả về 0x1234và cuộc gọi thứ hai 0x5678, thì bạn nhận được 0x12345678. Đó là con số duy nhất mà bạn có thể nhận được bắt đầu 0x1234, bởi vì số tiếp theo sẽ luôn như vậy 0x5678. Bạn nhận được kết quả 32 bit, nhưng bạn chỉ có 32768 số khả thi.
user694733

@ user694733 một trình tạo số ngẫu nhiên tốt có chu kỳ lớn hơn số đầu ra mà nó có thể tạo, vì vậy 0x1234 không phải lúc nào cũng được theo sau bởi 0x5678.
Mark Ransom

9

Nếu bạn có thể, hãy sử dụng Boost . Tôi đã gặp may mắn với thư viện ngẫu nhiên của họ .

uniform_int nên làm những gì bạn muốn.


Tôi đã thực hiện một số công việc trên Uniform_int với một twister merseinne và không may là đối với một số phạm vi nhất định, các giá trị được trả về bởi same_int không đồng nhất như tôi mong đợi. Ví dụ: Uniform_int <> (0, 3) có xu hướng tạo ra nhiều số 0 hơn số 1 hoặc số 2
ScaryAardvark

@ScaryAardvark nghe có vẻ như là một cách triển khai tồi uniform_int. Khá dễ dàng để tạo ra một đầu ra không thiên vị, đã có nhiều câu hỏi ở đây chứng minh phương pháp này.
Mark Ransom

@Mark Ransom. Vâng, tôi hoàn toàn đồng ý.
ScaryAardvark

8

Nếu bạn lo lắng về tính ngẫu nhiên chứ không phải về tốc độ, bạn nên sử dụng phương pháp tạo số ngẫu nhiên an toàn. Có một số cách để thực hiện việc này ... Cách dễ nhất là sử dụng Trình tạo số ngẫu nhiên của OpenSSL .

Bạn cũng có thể tự viết bằng thuật toán mã hóa (như AES ). Bằng cách chọn một hạt giống và một IV và sau đó liên tục mã hóa lại đầu ra của chức năng mã hóa. Sử dụng OpenSSL dễ dàng hơn, nhưng ít tốn kém hơn.


Tôi không thể sử dụng bất kỳ thư viện của bên thứ ba nào? Tôi chỉ bị giới hạn trong C ++.
anand

Sau đó đi theo con đường nam tính, thực hiện AES hoặc một số thuật toán mã hóa khác.
SoapBox

2
RC4 là mã nhỏ và đủ ngẫu nhiên cho tất cả các mục đích thực tế (ngoại trừ WEP, nhưng đó không hoàn toàn là lỗi của RC4). Ý tôi là, nó là một đoạn mã cực kỳ tầm thường. Giống như, 20 dòng hoặc lâu hơn. Mục nhập Wikipedia có mã giả.
Steve Jessop

4
Tại sao bạn không thể sử dụng mã của bên thứ ba? Nếu đây là một câu hỏi bài tập về nhà, bạn nên nói như vậy, vì nhiều người thà đưa ra những gợi ý hữu ích thay vì đưa ra các giải pháp hoàn chỉnh trong trường hợp này. Nếu đó không phải là bài tập về nhà, hãy đá anh chàng nói "không có mã bên thứ 3", bởi vì anh ta là một kẻ ngu ngốc.
DevSolar

Liên kết trực tiếp hơn đến tài liệu hàm OpenSSL rand (): openssl.org/docs/crypto/rand.html#
DevSolar

5

Bạn nên xem xét RAND_MAXtrình biên dịch / môi trường cụ thể của mình. Tôi nghĩ bạn sẽ thấy những kết quả này nếu rand()tạo ra một số 16 bit ngẫu nhiên. (dường như bạn đang giả định rằng nó sẽ là một số 32 bit).

Tôi không thể hứa đây là câu trả lời, nhưng vui lòng đăng giá trị của bạn RAND_MAXvà một chút chi tiết hơn về môi trường của bạn.


3

Kiểm tra những gì RAND_MAXcó trên hệ thống của bạn - Tôi đoán nó chỉ có 16 bit và phạm vi của bạn quá lớn đối với nó.

Ngoài ra, hãy xem thảo luận này về: Tạo số nguyên ngẫu nhiên trong phạm vi mong muốn và những lưu ý về việc sử dụng (hoặc không) hàm C rand () .


Ok RAND_MAX là 32767. Tôi đang sử dụng nền tảng C ++ windows .. Có phương pháp nào khác để tạo số ngẫu nhiên có phân phối đồng nhất không?
anand

2

Đây không phải là mã, nhưng logic này có thể giúp bạn.

static double rnd(void)
{
   return (1.0 / (RAND_MAX + 1.0) * ((double)(rand())) );
}

static void InitBetterRnd(unsigned int seed)
{
    register int i;
    srand( seed );
    for( i = 0; i < POOLSIZE; i++){
        pool[i] = rnd();
    }
}

 // This function returns a number between 0 and 1
 static double rnd0_1(void)
 {
    static int i = POOLSIZE-1;
    double r;

    i = (int)(POOLSIZE*pool[i]);
    r = pool[i];
    pool[i] = rnd();
    return (r);
}

2

Nếu bạn muốn các con số được phân phối đồng đều trên phạm vi, bạn nên chia phạm vi của mình thành một số phần bằng nhau đại diện cho số điểm bạn cần. Sau đó, lấy một số ngẫu nhiên với giá trị tối thiểu / tối đa cho mỗi phần.

Một lưu ý khác, bạn có thể không nên sử dụng rand()vì nó không thực sự tốt trong việc tạo ra các số ngẫu nhiên. Tôi không biết bạn đang chạy trên nền tảng nào, nhưng có lẽ có một chức năng tốt hơn mà bạn có thể gọi như vậy random().


1

Điều này sẽ cung cấp phân phối đồng đều trên phạm vi [low, high)mà không sử dụng số nổi, miễn là phạm vi tổng thể nhỏ hơn RAND_MAX.

uint32_t rand_range_low(uint32_t low, uint32_t high)
{
    uint32_t val;
    // only for 0 < range <= RAND_MAX
    assert(low < high);
    assert(high - low <= RAND_MAX);

    uint32_t range = high-low;
    uint32_t scale = RAND_MAX/range;
    do {
        val = rand();
    } while (val >= scale * range); // since scale is truncated, pick a new val until it's lower than scale*range
    return val/scale + low;
}

và đối với các giá trị lớn hơn RAND_MAX, bạn muốn một cái gì đó như

uint32_t rand_range(uint32_t low, uint32_t high)
{
    assert(high>low);
    uint32_t val;
    uint32_t range = high-low;
    if (range < RAND_MAX)
        return rand_range_low(low, high);
    uint32_t scale = range/RAND_MAX;
    do {
        val = rand() + rand_range(0, scale) * RAND_MAX; // scale the initial range in RAND_MAX steps, then add an offset to get a uniform interval
    } while (val >= range);
    return val + low;
}

Đại khái đây là cách std :: Uniform_int_distribution hoạt động.


0

Theo bản chất của chúng, một mẫu nhỏ các số ngẫu nhiên không nhất thiết phải được phân phối đồng đều. Rốt cuộc thì chúng là ngẫu nhiên. Tôi đồng ý rằng nếu một trình tạo số ngẫu nhiên đang tạo ra các số dường như được nhóm một cách nhất quán, thì có lẽ đã xảy ra lỗi với nó.

Nhưng hãy nhớ rằng sự ngẫu nhiên không nhất thiết phải đồng nhất.

Chỉnh sửa: Tôi đã thêm "mẫu nhỏ" để làm rõ.


"phân phối đồng đều" có một ý nghĩa được xác định rõ và các bộ tạo ngẫu nhiên tiêu chuẩn thường đến gần.
peterchen

Vâng, bạn nói đúng, bộ tạo số ngẫu nhiên sẽ tạo ra sản lượng theo thời gian nói chung là đồng nhất trong phân phối của nó. Tôi đoán quan điểm của tôi là trong một số ít trường hợp (6 như được hiển thị trong ví dụ), đầu ra không phải lúc nào cũng đồng nhất.
Kluge

Kluge nói đúng. Sự phân bố đồng đều trong một mẫu nhỏ cho thấy rằng mẫu đó chắc chắn không phải ngẫu nhiên.
Bill the Lizard vào

1
Bill, nó chỉ ra không có điều đó. Các mẫu nhỏ hầu hết là vô nghĩa, nhưng nếu RNG được cho là đồng đều và đầu ra là đồng nhất, tại sao điều đó lại tệ hơn một mẫu nhỏ không đồng nhất?
Dan Dyer

2
Các phân phối đáng kể theo cách nào cũng cho thấy tính không ngẫu nhiên: Tôi nghĩ Bill chỉ có nghĩa là 6 kết quả cách đều nhau cũng sẽ bị nghi ngờ. Trong OP, 6 giá trị nằm trong phạm vi 32k / 4M, hoặc <1% phạm vi mong muốn. Khả năng đây là một dương tính giả là quá nhỏ để tranh cãi.
Steve Jessop

0

Giải pháp được đưa ra bởi người đàn ông 3 rand cho một số từ 1 đến 10 là:

j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));

Trong trường hợp của bạn, nó sẽ là:

j = min + (int) ((max-min+1) * (rand() / (RAND_MAX + 1.0)));

Tất nhiên, đây không phải là sự ngẫu nhiên hay đồng nhất hoàn hảo như một số thông báo khác đang chỉ ra, nhưng điều này là đủ cho hầu hết các trường hợp.


1
Đây chỉ đơn thuần là sắp xếp lại phân phối cho xuất hiện nhiều hơn, nhưng nó không thực sự nữa ngay cả đối với các dãy lớn (chẳng hạn như trường hợp của OP)
Mooing Duck

0

@Giải pháp ((double) rand() / (RAND_MAX+1)) * (max-min+1) + min

Cảnh báo : Đừng quên do độ giãn và lỗi độ chính xác có thể xảy ra (ngay cả khi RAND_MAX đủ lớn), bạn sẽ chỉ có thể tạo "thùng" phân bố đồng đều chứ không phải tất cả các số trong [min, max].


@Solution: Bigrand

Cảnh báo : Lưu ý rằng điều này làm tăng gấp đôi các bit, nhưng vẫn không thể tạo ra tất cả các số trong phạm vi của bạn nói chung, tức là không nhất thiết phải đúng rằng BigRand () sẽ tạo ra tất cả các số trong phạm vi của nó.


Thông tin : Phương pháp tiếp cận của bạn (modulo) là "ổn" miễn là phạm vi của rand () vượt quá phạm vi khoảng thời gian của bạn và rand () là "đồng nhất". Lỗi cho nhiều nhất các số tối thiểu đầu tiên là 1 / (RAND_MAX +1).

Ngoài ra, tôi cũng khuyên bạn nên chuyển sang gói ngẫu nhiên mới e trong C ++ 11, gói này cung cấp nhiều loại triển khai hơn và tốt hơn so với rand ().


0

Đây là giải pháp tôi đã nghĩ ra:

#include "<stdlib.h>"

int32_t RandomRange(int32_t min, int32_t max) {
    return (rand() * (max - min + 1) / (RAND_MAX + 1)) + min;
}

Đây là một giải pháp xô, về mặt khái niệm tương tự như các giải pháp sử dụng rand() / RAND_MAXđể lấy phạm vi dấu phẩy động từ 0-1 và sau đó làm tròn nó thành một nhóm. Tuy nhiên, nó sử dụng phép toán số nguyên hoàn toàn và tận dụng lợi thế của phép chia số nguyên để làm tròn giá trị xuống nhóm gần nhất.

Nó đưa ra một vài giả định. Đầu tiên, nó giả định rằng RAND_MAX * (max - min + 1)sẽ luôn phù hợp với int32_t. Nếu RAND_MAXlà 32767 và sử dụng phép tính int 32 bit, phạm vi tối đa bạn có thể có là 32767. Nếu việc triển khai của bạn có RAND_MAX lớn hơn nhiều, bạn có thể khắc phục điều này bằng cách sử dụng số nguyên lớn hơn (như int64_t) cho phép tính. Thứ hai, nếu int64_tđược sử dụng nhưng RAND_MAXvẫn là 32767, ở phạm vi lớn hơn RAND_MAXbạn sẽ bắt đầu nhận được "lỗ hổng" trong các số đầu ra có thể. Đây có lẽ là vấn đề lớn nhất với bất kỳ giải pháp nào bắt nguồn từ việc mở rộng quy mô rand().

Tuy nhiên, thử nghiệm qua một số lượng lớn các lần lặp lại cho thấy phương pháp này rất đồng nhất đối với các phạm vi nhỏ. Tuy nhiên, có thể (và có khả năng) về mặt toán học, điều này có một số sai lệch nhỏ và có thể phát sinh các vấn đề khi phạm vi tiếp cận RAND_MAX. Hãy tự mình kiểm tra và quyết định xem nó có đáp ứng được nhu cầu của bạn không.


-1

Tất nhiên, đoạn mã sau sẽ không cung cấp cho bạn số ngẫu nhiên mà là số giả ngẫu nhiên. Sử dụng mã sau

#define QUICK_RAND(m,n) m + ( std::rand() % ( (n) - (m) + 1 ) )

Ví dụ:

int myRand = QUICK_RAND(10, 20);

Bạn phải gọi

srand(time(0));  // Initialize random number generator.

nếu không các con số sẽ không gần như ngẫu nhiên.


1
Câu hỏi đặt ra là yêu cầu sự phân bố đồng đều. Giải pháp được đề xuất này sẽ không tạo ra một phân phối đồng đều. Thư viện C ++ tiêu chuẩn có các phương tiện để tạo số giả ngẫu nhiên . Những làm cung cấp phân phối thống nhất, nếu có yêu cầu.
IInspectable

-3

Tôi vừa tìm thấy cái này trên Internet. Điều này sẽ hoạt động:

DWORD random = ((min) + rand()/(RAND_MAX + 1.0) * ((max) - (min) + 1));

Vui lòng làm rõ bạn cần chúng để làm gì, có rất nhiều thuật toán cho PRNG ngoài kia. Ngoài ra, sẽ dễ dàng hơn nếu bạn chỉnh sửa câu hỏi chính của mình thay vì đăng câu trả lời.
peterchen

Điều này phù hợp nhất với tôi ... Tôi có thể nhận được các số ngẫu nhiên được phân phối tốt hơn với công thức này ..
anand

4
Nếu phạm vi của bạn vượt quá RAND_MAX, kết quả có thể không đồng nhất. Có nghĩa là, có những giá trị trong phạm vi sẽ không được đại diện cho dù bạn gọi hàm của bạn bao nhiêu lần.
dmckee --- ex-moderator kitten

4
Ngoài ra, nếu max và min đều là int unsigned, và min là 0 và max là MAX_UINT, thì ((max) - (min) +1) sẽ là 0 và kết quả luôn là 0. Hãy coi chừng tràn khi làm loại toán này! Như đã lưu ý bởi dmckee, điều này kéo dài phân phối trên phạm vi đích, nhưng không đảm bảo nhiều hơn giá trị duy nhất RAND_MAX.
jesup
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.