Tạo số float ngẫu nhiên


271

Làm cách nào để tạo số float ngẫu nhiên trong C ++?

Tôi nghĩ rằng tôi có thể lấy số nguyên rand và chia nó cho một cái gì đó, điều đó có đủ không?


2
Nó phụ thuộc vào những gì bạn muốn số cho, và làm thế nào ngẫu nhiên. thông thường rand () sẽ cho 15 bit ngẫu nhiên, nhưng float có độ chính xác 23 bit, vì vậy nó sẽ bỏ lỡ một số giá trị.
Pete Kirkham

1
Tôi đã cập nhật câu trả lời của mình để bao gồm tất cả các tùy chọn chính có sẵn và lựa chọn của tôi để tập trung vào randomtiêu đề được thêm vào trong C ++ 11 được củng cố thêm bằng tài liệu tiêu chuẩn N3924: Discouraging rand () trong C ++ 14 . Tôi bao gồm rand()trong câu trả lời của tôi cho hầu hết các cân nhắc lịch sử nhưng cũng nhận ra ứng dụng kế thừa tồn tại.
Shafik Yaghmour

Câu trả lời của tôi bao gồm làm thế nào để tránh nhận được cùng một số mỗi lần với <random>tiêu đề
Andreas DM

Câu trả lời:


381

rand()có thể được sử dụng để tạo các số giả ngẫu nhiên trong C ++. Kết hợp với RAND_MAXvà một chút toán học, bạn có thể tạo các số ngẫu nhiên trong bất kỳ khoảng tùy ý nào bạn chọn. Điều này là đủ cho mục đích học tập và các chương trình đồ chơi. Nếu bạn cần số thực sự ngẫu nhiên với phân phối bình thường, bạn sẽ cần sử dụng một phương pháp nâng cao hơn.


Điều này sẽ tạo ra một số từ 0,0 đến 1,0, bao gồm.

float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);

Điều này sẽ tạo ra một số từ 0,0 đến một số tùy ý float, X:

float r2 = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/X));

Điều này sẽ tạo ra một số từ một số tùy ý LOđến một số tùy ý HI:

float r3 = LO + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(HI-LO)));

Lưu ý rằng rand()hàm thường sẽ không đủ nếu bạn cần các số thực sự ngẫu nhiên.


Trước khi gọi rand(), trước tiên bạn phải "gieo" trình tạo số ngẫu nhiên bằng cách gọi srand(). Điều này nên được thực hiện một lần trong khi chạy chương trình của bạn - không phải một lần mỗi khi bạn gọi rand(). Điều này thường được thực hiện như thế này:

srand (static_cast <unsigned> (time(0)));

Để gọi randhoặc srandbạn phải #include <cstdlib>.

Để gọi time, bạn phải #include <ctime>.


22
Đừng quên gieo hạt trước!
Klaim

14
Tốt nhất cần lưu ý rằng cả hai giới hạn là bao gồm.
dmckee --- ex-moderator mèo con

14
Câu trả lời này là sai lệch. Nó đã được bảo hiểm tại Đi bản địa 2013 vào tuần trước; rand () Được coi là có hại, channel9.msdn.com/Events/GoingNative/2013/iêu cho một lời giải thích rất chi tiết.
Ade Miller

14
Tôi không hiểu tại sao rất nhiều người ủng hộ câu trả lời này. Đó là toán học không chính xác. RAND_MAX là một số rất nhỏ (thường là 2 ^ 16). Điều đó có nghĩa là từ 23 bit của dấu phẩy động bạn chỉ thực hiện 15 ngẫu nhiên. Những người khác có lẽ sẽ bằng không. Bạn thực sự sẽ nhận được số ngẫu nhiên trong phân phối đồng đều nhưng độ chính xác thấp. Ví dụ: trình tạo ngẫu nhiên của bạn có thể tạo 0,00001 và 0,00002 nhưng không thể tạo 0,000017. Vì vậy, bạn có phân phối đồng đều nhưng độ chính xác thấp (độ chính xác thấp hơn 256 lần so với điểm nổi thực tế).
DanielHsH

10
@DanielHsH: OP đặc biệt hỏi cơ chế nào có thể được sử dụng để tạo ra các phao ngẫu nhiên với rand(). Câu hỏi này, và câu trả lời của tôi, đặc biệt tập trung vào việc học những điều cơ bản và không quan tâm đến mức độ chính xác cao. Bạn phải học cách đi bộ trước khi bạn có thể học chạy.
John Dibling

137

C ++ 11 cung cấp cho bạn rất nhiều tùy chọn mới với random. Bài viết kinh điển về chủ đề này sẽ là N3551, Tạo số ngẫu nhiên trong C ++ 11

Để biết lý do tại sao việc sử dụng rand()có thể có vấn đề, hãy xem tài liệu trình bày có hại của rand () được coi là có hại của Stephan T. Lavavej được đưa ra trong sự kiện Đi 2013 . Các slide trong các ý kiến ​​nhưng đây là một liên kết trực tiếp .

Tôi cũng bao gồm boostcũng như sử dụng randvì mã kế thừa vẫn có thể yêu cầu hỗ trợ của nó.

Ví dụ dưới đây được chắt lọc từ trang web cppreference và sử dụng công cụ std :: mersenne_twister_enginestd :: thống_real_distribution tạo ra các số trong [0,10)khoảng, với các công cụ và phân phối khác được nhận xét ( xem nó trực tiếp ):

#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>

int main()
{
    std::random_device rd;

    //
    // Engines 
    //
    std::mt19937 e2(rd());
    //std::knuth_b e2(rd());
    //std::default_random_engine e2(rd()) ;

    //
    // Distribtuions
    //
    std::uniform_real_distribution<> dist(0, 10);
    //std::normal_distribution<> dist(2, 2);
    //std::student_t_distribution<> dist(5);
    //std::poisson_distribution<> dist(2);
    //std::extreme_value_distribution<> dist(0,2);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::floor(dist(e2))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

đầu ra sẽ tương tự như sau:

0 ****
1 ****
2 ****
3 ****
4 *****
5 ****
6 *****
7 ****
8 *****
9 ****

Đầu ra sẽ thay đổi tùy thuộc vào phân phối bạn chọn, vì vậy nếu chúng tôi quyết định sử dụng std :: normal_distribution với giá trị 2cho cả trung bìnhstddev, ví dụ dist(2, 2)thay vì đầu ra sẽ tương tự như vậy ( xem trực tiếp ):

-6 
-5 
-4 
-3 
-2 **
-1 ****
 0 *******
 1 *********
 2 *********
 3 *******
 4 ****
 5 **
 6 
 7 
 8 
 9 

Sau đây là phiên bản sửa đổi của một số mã được trình bày trong N3551( xem trực tiếp ):

#include <algorithm>
#include <array>
#include <iostream>
#include <random>

std::default_random_engine & global_urng( )
{
    static std::default_random_engine u{};
    return u ;
}

void randomize( )
{
    static std::random_device rd{};
    global_urng().seed( rd() );
}

int main( )
{
  // Manufacture a deck of cards:
  using card = int;
  std::array<card,52> deck{};
  std::iota(deck.begin(), deck.end(), 0);

  randomize( ) ;  

  std::shuffle(deck.begin(), deck.end(), global_urng());
  // Display each card in the shuffled deck:
  auto suit = []( card c ) { return "SHDC"[c / 13]; };
  auto rank = []( card c ) { return "AKQJT98765432"[c % 13]; };

  for( card c : deck )
      std::cout << ' ' << rank(c) << suit(c);

   std::cout << std::endl;
}

Kết quả sẽ tương tự như:

5H 5S NHƯ 9S 4D 6H TH 6D KH 2S QS 9H 8H 3D KC TD 7H 2D KS 3C TC 7D 4C QH QC QĐ JD AH JC AC KD 9D 5C 2H 4H 9C 8C JH 5D 4S 7C AD 3S 8S TS 2C 8D 3H 6C 7S 6S

Tăng cường

Tất nhiên Boost.Random luôn là một tùy chọn, ở đây tôi đang sử dụng boost :: Random :: thống_real_distribution :

#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>

int main()
{
    boost::random::mt19937 gen;
    boost::random::uniform_real_distribution<> dist(0, 10);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::floor(dist(gen))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

rand ()

Nếu bạn phải sử dụng rand()thì chúng ta có thể truy cập Câu hỏi thường gặp về C để được hướng dẫn về Cách tạo số ngẫu nhiên dấu phẩy động? , về cơ bản đưa ra một ví dụ tương tự như điều này để tạo một khoảng [0,1):

#include <stdlib.h>

double randZeroToOne()
{
    return rand() / (RAND_MAX + 1.);
}

và để tạo một số ngẫu nhiên trong phạm vi từ [M,N):

double randMToN(double M, double N)
{
    return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
}

1
bạn có thể sửa chữa randMToNxin vui lòng? hoặc lưu ý rằng đó là [M,N]hoặc thêm lại + 1.từ trên randZeroToOne. -> nghĩ về việc gọi nó như thế này:randMToN(0.0, 1.0);
BeyelerStudios

1
Cũng cẩn thận với chia cho số 0 tại (N-M). Một cách hay để đối phó với lỗi này được tìm thấy ở đây: stackoverflow.com/questions/33058848/
Khăn

61

Hãy xem Boost.Random . Bạn có thể làm một cái gì đó như thế này:

float gen_random_float(float min, float max)
{
    boost::mt19937 rng;
    boost::uniform_real<float> u(min, max);
    boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > gen(rng, u);
    return gen();
}

Chơi xung quanh, bạn có thể làm tốt hơn khi vượt qua cùng một đối tượng mt19937 thay vì xây dựng một đối tượng mới mỗi lần, nhưng hy vọng bạn có ý tưởng.


1
thống nhất_real sử dụng khoảng thời gian nửa mở [tối thiểu, tối đa), có nghĩa là bạn sẽ nhận được giá trị tối thiểu của mình nhưng sẽ không bao giờ đạt được giá trị tối đa. Đó là một cái gì đó để xem xét, mặc dù nếu bạn làm tròn theo một cách nào đó, bạn có thể khắc phục vấn đề này.
TehWan

20
Đây là một phần của C ++ 11.
Tomas Andrle

@Wolf trong các ứng dụng thực tế, tỷ lệ trúng bất kỳ giá trị dấu phẩy động cụ thể nào thấp đến mức không thành vấn đề nếu điểm cuối được bao gồm hoặc loại trừ. Nếu bạn cần maxnhưng có thể sử dụng kết thúc mở min, bạn có thể đảo ngược khoảng cách dễ dàng : return min + max - gen();.
Mark Ransom

26

Trong hiện đại, c++bạn có thể sử dụng <random>tiêu đề đi kèm c++11.
Để có được ngẫu nhiên float, bạn có thể sử dụng std::uniform_real_distribution<>.

Bạn có thể sử dụng một hàm để tạo các số và nếu bạn không muốn các số đó giống nhau mọi lúc, hãy đặt công cụ và phân phối thành static.
Thí dụ:

float get_random()
{
    static std::default_random_engine e;
    static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
    return dis(e);
}

Thật lý tưởng khi đặt các vật floattrong một thùng chứa như std::vector:

int main()
{
    std::vector<float> nums;
    for (int i{}; i != 5; ++i) // Generate 5 random floats
        nums.emplace_back(get_random());

    for (const auto& i : nums) std::cout << i << " ";
}

Ví dụ đầu ra:

0.0518757 0.969106 0.0985112 0.0895674 0.895542

std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1về mặt kỹ thuật không chính xác, 1.0 sẽ không bao giờ được tạo, xem en.cppreference.com/w/cpp/numeric/random/ mẹo To create a distribution over the closed interval [a,b], std::nextafter(b, std::numeric_limits<RealType>::max()) may be used as the second parameter.
Troyseph

1
Đây phải là câu trả lời được chấp nhận, thật đáng sợ vào năm 2020.
Alex

25

Gọi mã với hai floatgiá trị, mã hoạt động trong bất kỳ phạm vi.

float rand_FloatRange(float a, float b)
{
    return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}

Có thể đáng nói rằng đây là trường hợp sử dụng tiềm năng cho fmaf()(hoặc fma()quá tải float trong C ++) trong C99 hoặc C ++ 11, có thể bảo vệ độ chính xác cao hơn. Như trong fmaf((float)rand() / RAND_MAX, b - a, a).
Tim as

22

Nếu bạn đang sử dụng C ++ chứ không phải C, thì hãy nhớ rằng trong báo cáo kỹ thuật 1 (TR1) và trong bản nháp C ++ 0x, họ đã thêm các phương tiện cho trình tạo số ngẫu nhiên trong tệp tiêu đề, tôi tin rằng nó giống hệt với Boost. Thư viện ngẫu nhiên và chắc chắn linh hoạt và "hiện đại" hơn chức năng thư viện C, rand.

Cú pháp này cung cấp khả năng chọn một trình tạo (như mersenne twister mt19937) và sau đó chọn phân phối (bình thường, bernoulli, nhị thức, v.v.).

Cú pháp như sau (không biết xấu hổ mượn từ trang web này ):

  #include <iostream>
  #include <random>

  ...

  std::tr1::mt19937 eng;  // a core engine class 
  std::tr1::normal_distribution<float> dist;     

  for (int i = 0; i < 10; ++i)        
      std::cout << dist(eng) << std::endl;

2
Điều này hiện có trong C ++ 11, cũng có thể khởi tạo dist với giá trị tối thiểu và tối đa.
Étienne

Đối với tôi, việc đặt min và max trong trình khởi tạo có vẻ kỳ lạ và cung cấp trình tạo khi nhận giá trị - tôi thích nếu nó là cách khác, ồ tốt.
yoyo

6

Trên một số hệ thống (Windows có VC lò xo, hiện tại), RAND_MAXrất nhỏ, i. e. chỉ 15 bit. Khi chia cho RAND_MAXbạn chỉ tạo ra một mantissa 15 bit thay vì 23 bit có thể. Điều này có thể hoặc không phải là một vấn đề đối với bạn, nhưng bạn đang bỏ lỡ một số giá trị trong trường hợp đó.

Ồ, chỉ cần lưu ý rằng đã có một bình luận cho vấn đề đó. Dù sao, đây là một số mã có thể giải quyết điều này cho bạn:

float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);

Chưa được kiểm tra, nhưng có thể hoạt động :-)


Điều gì về float r = (float) ((rand () << 9) | rand ()) / RAND_MAX? (cũng chưa được kiểm tra)
Bẫy

Argh, xin lỗi, chia cho RAND_MAX sẽ không đưa bạn đến bất cứ đâu ... toàn bộ vấn đề của thủ thuật này là có một cái gì đó lớn hơn RAND_MAX ... cũng đã sửa nó cho tôi.
Joey

2
Hãy cẩn thận về việc soạn các số ngẫu nhiên mà không có lý thuyết ... các cuộc gọi liên tiếp đến rand () có thể không hoàn toàn độc lập. Gợi ý: nếu là trình tạo đồng quy tuyến tính, hãy xem bit thấp trong các cuộc gọi liên tiếp: nó thay đổi giữa 0 và 1.
RBerteig

Tôi biết. Đối với một số ứng dụng, điều này có thể là đủ, mặc dù. Nhưng có, bạn có lẽ nên sử dụng nhiều hơn chỉ hai cuộc gọi trong trường hợp này. Không có viên đạn bạc nào trong trường hợp này, bạn thậm chí không thể dựa vào nó là LCG. Các PRNG khác có bit cao yếu. Giải pháp Boost nên là tốt nhất ở đây.
Joey

(nb làm điều tương tự)
Joey

4

drand48(3)là cách tiêu chuẩn POSIX. GLibC cũng cung cấp một phiên bản reentrant , drand48_r(3).

Chức năng đã được tuyên bố là lỗi thời trong SVID 3 nhưng không có sự thay thế thích hợp nào được cung cấp, do đó, IEEE Std 1003.1-2013 vẫn bao gồm nó và không có ghi chú rằng nó sẽ sớm xuất hiện ở mọi nơi.

Trong Windows, cách tiêu chuẩn là CryptGenRandom () .


2

Tôi không hài lòng với bất kỳ câu trả lời nào cho đến nay nên tôi đã viết một hàm float ngẫu nhiên mới. Nó đưa ra các giả định bitwise về kiểu dữ liệu float. Nó vẫn cần một hàm rand () với ít nhất 15 bit ngẫu nhiên.

//Returns a random number in the range [0.0f, 1.0f).  Every
//bit of the mantissa is randomized.
float rnd(void){
  //Generate a random number in the range [0.5f, 1.0f).
  unsigned int ret = 0x3F000000 | (0x7FFFFF & ((rand() << 8) ^ rand()));
  unsigned short coinFlips;

  //If the coin is tails, return the number, otherwise
  //divide the random number by two by decrementing the
  //exponent and keep going. The exponent starts at 63.
  //Each loop represents 15 random bits, a.k.a. 'coin flips'.
  #define RND_INNER_LOOP() \
    if( coinFlips & 1 ) break; \
    coinFlips >>= 1; \
    ret -= 0x800000
  for(;;){
    coinFlips = rand();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    //At this point, the exponent is 60, 45, 30, 15, or 0.
    //If the exponent is 0, then the number equals 0.0f.
    if( ! (ret & 0x3F800000) ) return 0.0f;
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
  }
  return *((float *)(&ret));
}

7
cách tiếp cận thú vị, tôi muốn upvote nhưng, tôi thực sự không hiểu những gì đang xảy ra
hasen

2

Theo ý kiến ​​của tôi, câu trả lời ở trên có thể đưa ra một số float 'ngẫu nhiên', nhưng không ai trong số chúng thực sự là một float ngẫu nhiên (tức là họ bỏ lỡ một phần của biểu diễn float). Trước khi tôi bắt đầu triển khai, trước tiên hãy xem định dạng chuẩn ANSI / IEEE cho số float:

| ký (1-bit) | e (8 bit) | f (23-bit) |

số được biểu thị bằng từ này là (-1 * dấu) * 2 ^ e * 1.f

lưu ý số 'e' là số thiên vị (có độ lệch là 127), do đó dao động từ -127 đến 126. Hàm đơn giản nhất (và thực sự ngẫu nhiên nhất) là chỉ ghi dữ liệu của một int ngẫu nhiên vào một dấu phẩy, do đó

int tmp = rand();
float f = (float)*((float*)&tmp);

lưu ý rằng nếu bạn làm điều float f = (float)rand();đó sẽ chuyển đổi số nguyên thành số float (do đó 10 sẽ trở thành 10.0).

Vì vậy, bây giờ nếu bạn muốn giới hạn giá trị tối đa, bạn có thể làm một cái gì đó như (không chắc chắn nếu điều này hoạt động)

int tmp = rand();
float f = *((float*)&tmp);
tmp = (unsigned int)f       // note float to int conversion!
tmp %= max_number;
f -= tmp;

nhưng nếu bạn nhìn vào cấu trúc của float, bạn có thể thấy rằng giá trị tối đa của float là (xấp xỉ 2 ^ 127, lớn hơn giá trị tối đa của int (2 ^ 32) do đó loại bỏ một phần đáng kể của những con số có thể được đại diện bởi một float. Đây là lần thực hiện cuối cùng của tôi:

/**
 * Function generates a random float using the upper_bound float to determine 
 * the upper bound for the exponent and for the fractional part.
 * @param min_exp sets the minimum number (closest to 0) to 1 * e^min_exp (min -127)
 * @param max_exp sets the maximum number to 2 * e^max_exp (max 126)
 * @param sign_flag if sign_flag = 0 the random number is always positive, if 
 *              sign_flag = 1 then the sign bit is random as well
 * @return a random float
 */
float randf(int min_exp, int max_exp, char sign_flag) {
    assert(min_exp <= max_exp);

    int min_exp_mod = min_exp + 126;

    int sign_mod = sign_flag + 1;
    int frac_mod = (1 << 23);

    int s = rand() % sign_mod;  // note x % 1 = 0
    int e = (rand() % max_exp) + min_exp_mod;
    int f = rand() % frac_mod;

    int tmp = (s << 31) | (e << 23) | f;

    float r = (float)*((float*)(&tmp));

    /** uncomment if you want to see the structure of the float. */
//    printf("%x, %x, %x, %x, %f\n", (s << 31), (e << 23), f, tmp, r);

    return r;
}

sử dụng hàm randf(0, 8, 0)này sẽ trả về một số ngẫu nhiên trong khoảng từ 0,0 đến 255,0


1
bạn có một sai lầm rand ()% frac_mod sẽ không hoạt động vì MAX_RAND thường thấp hơn (1 << 23).
DanielHsH

Tôi phải thừa nhận rằng tôi không biết kích thước chính xác của MAX_RAND. Không bao giờ nó sẽ vẫn hoạt động, nó có thể là một tuyên bố vô dụng, nhưng nó vẫn sẽ hoạt động. 8% 10 = 8 như vậy là tốt, nhưng nếu MAX_RAND luôn nhỏ hơn thì (1 << 23) bạn thực sự có thể xóa nó.
dùng2546926

2
Không, bạn hơi sai một chút. RandMax thường là ~ 65.000. Điều đó có nghĩa là từ 23 bit bạn chỉ thực hiện 15 ngẫu nhiên. Những người khác có lẽ sẽ bằng không. Bạn thực sự sẽ nhận được số ngẫu nhiên nhưng độ chính xác thấp. Ví dụ: trình tạo ngẫu nhiên của bạn có thể tạo 0,001 và 0,002 nhưng không thể tạo 0,0017. Vì vậy, bạn có một phân phối đồng đều nhưng độ chính xác thấp (độ chính xác thấp hơn 256 lần so với float).
DanielHsH

Có hai sai lầm trong câu trả lời này. Sửa phạm vi số mũ: int e = (rand() % (max_exp - min_exp)) + min_exp_mod;và mantissa: int f = (int)(frac_mod * (float)rand() / RAND_MAX);thay thế các dòng tương ứng của chúng ở trên. Lưu ý rằng lỗi mantissa là rất lớn: đối với RAND_MAXnhỏ hơn, 1 << 23bạn chỉ chọn ngẫu nhiên các bit có ý nghĩa thấp hơn và nhận 0s cho các bit quan trọng nhất mọi lúc!
BeyelerStudios

2

Nếu bạn biết rằng định dạng dấu phẩy động của bạn là IEEE 754 (gần như tất cả các CPU hiện đại bao gồm Intel và ARM) thì bạn có thể tạo một số dấu phẩy động ngẫu nhiên từ một số nguyên ngẫu nhiên bằng phương pháp bit-khôn ngoan. Điều này chỉ nên được xem xét nếu bạn không có quyền truy cập vào C ++ 11 randomhoặc Boost.Randomcả hai đều tốt hơn nhiều.

float rand_float()
{
    // returns a random value in the range [0.0-1.0)

    // start with a bit pattern equating to 1.0
    uint32_t pattern = 0x3f800000;

    // get 23 bits of random integer
    uint32_t random23 = 0x7fffff & (rand() << 8 ^ rand());

    // replace the mantissa, resulting in a number [1.0-2.0)
    pattern |= random23;

    // convert from int to float without undefined behavior
    assert(sizeof(float) == sizeof(uint32_t));
    char buffer[sizeof(float)];
    memcpy(buffer, &pattern, sizeof(float));
    float f;
    memcpy(&f, buffer, sizeof(float));

    return f - 1.0;
}

Điều này sẽ cung cấp một phân phối tốt hơn so với một phân chia sử dụng.


8
Tôi không chắc tại sao bạn lại nói điều này sẽ mang lại "phân phối tốt hơn". Trong thực tế, điều này sẽ cung cấp chính xác phân phối giống như chỉ return (float)random23 / (1 << 23). (Vâng, tôi chỉ thử nghiệm này , thay đổi chức năng của bạn để tận random32như một tham số và chạy nó cho tất cả các giá trị từ zero lên đến (1 << 23)-1Và vâng, phương pháp của bạn không thực sự cung cấp cho chính xác kết quả tương tự như bộ phận bằng. 1 << 23.)
Ilmari Karonen

1

Đối với C ++, nó có thể tạo các số float thực trong phạm vi được chỉ định bởi distbiến

#include <random>  //If it doesnt work then use   #include <tr1/random>
#include <iostream>

using namespace std;

typedef std::tr1::ranlux64_base_01 Myeng; 
typedef std::tr1::normal_distribution<double> Mydist;

int main() { 
       Myeng eng; 
       eng.seed((unsigned int) time(NULL)); //initializing generator to January 1, 1970);
       Mydist dist(1,10); 

       dist.reset(); // discard any cached values 
       for (int i = 0; i < 10; i++)
       {
           std::cout << "a random value == " << (int)dist(eng) << std::endl; 
       }

       return (0);
}

1
Bạn vừa sao chép và dán mã từ câu trả lời này? stackoverflow.com/a/1118739/1538531
Derek

Thật ra tôi không ngạc nhiên khi thấy họ trông giống nhau đến mức nào! Nhưng tôi đã khởi tạo máy phát điện động cơ 1.1970.
Marco167

Đủ công bằng. Tôi đã nhận thấy rằng bạn đã khởi tạo trình tạo thành epoch, nhưng mã đó tương tự!
Derek

Tôi thấy hơi kỳ quặc khi đưa ra một ví dụ TR1, bạn có thể giải thích trong trường hợp ai đó sẽ phải sử dụng TR1 trái ngược với C ++ 11 không?
Shafik Yaghmour

0

rand () trả về một giá trị từ 0 đến RAND_MAX. Để có được một số ngẫu nhiên trong khoảng từ 0,0 đến 1, trước tiên, hãy trả int int bằng rand () cho một float, sau đó chia cho RAND_MAX.


0
#include <cstdint>
#include <cstdlib>
#include <ctime>

using namespace std;

/* single precision float offers 24bit worth of linear distance from 1.0f to 0.0f */
float getval() {
    /* rand() has min 16bit, but we need a 24bit random number. */
    uint_least32_t r = (rand() & 0xffff) + ((rand() & 0x00ff) << 16);
    /* 5.9604645E-8 is (1f - 0.99999994f), 0.99999994f is the first value less than 1f. */
    return (double)r * 5.9604645E-8;
}

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

Tôi không thể đăng hai câu trả lời, vì vậy đây là giải pháp thứ hai. log2 số ngẫu nhiên, độ lệch lớn đối với 0,0f nhưng thực sự là số float ngẫu nhiên 1.0f đến 0.0f.

#include <cstdint>
#include <cstdlib>
#include <ctime>

using namespace std;

float getval () {
    union UNION {
        uint32_t i;
        float f;
    } r;
    /* 3 because it's 0011, the first bit is the float's sign.
     * Clearing the second bit eliminates values > 1.0f.
     */
    r.i = (rand () & 0xffff) + ((rand () & 0x3fff) << 16);
    return r.f;
}

int main ()
{
    srand (time (NULL));
...
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.