Как создать случайные поплавки в С++?
Я думал, что могу взять целочисленный rand и разделить его на что-то, достаточно ли это было бы?
Как создать случайные поплавки в С++?
Я думал, что могу взять целочисленный rand и разделить его на что-то, достаточно ли это было бы?
rand()
может использоваться для генерации псевдослучайных чисел в С++. В сочетании с RAND_MAX
и небольшой математикой вы можете генерировать случайные числа в любом произвольном интервале, который вы выберете. Этого достаточно для учебных целей и игрушечных программ. Если вам нужны действительно случайные числа с нормальным распределением, вам нужно использовать более продвинутый метод.
Это приведет к генерации числа от 0.0 до 1.0 включительно.
float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
Это будет генерировать число от 0.0 до некоторого произвольного float
, X
:
float r2 = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/X));
Это будет генерировать число от некоторого произвольного LO
до некоторого произвольного HI
:
float r3 = LO + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(HI-LO)));
Обратите внимание, что функция rand()
часто бывает недостаточной, если вам нужны действительно случайные числа.
Перед вызовом rand()
вы должны сначала "засеять" генератор случайных чисел, вызвав srand()
. Это нужно делать один раз во время запуска вашей программы - не каждый раз, когда вы вызываете rand()
. Это часто делается следующим образом:
srand (static_cast <unsigned> (time(0)));
Для вызова rand
или srand
вы должны #include <cstdlib>
.
Чтобы позвонить time
, вы должны #include <ctime>
.
С++ 11 дает вам множество новых опций с random
. Каноническая статья на эту тему была бы N3551, Генерация случайных чисел в С++ 11
Чтобы понять, почему использование rand()
может быть проблематичным, см. rand() Рассмотрение вредоносного материала презентации Стефана Т. Лававей, данное во время GoingNative 2013. Слайды в комментариях, но вот прямая ссылка .
Я также охватываю boost
, а также используя rand
, поскольку устаревший код может по-прежнему нуждаться в его поддержке.
Приведенный ниже пример перегоняется с сайта cppreference и использует механизм std:: mersenne_twister_engine и std:: uniform_real_distribution, который генерирует числа в интервале [0,10)
, с комментариями других двигателей и дистрибутивов (видеть его вживую):
#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';
}
}
будет выглядеть примерно так:
0 ****
1 ****
2 ****
3 ****
4 *****
5 ****
6 *****
7 ****
8 *****
9 ****
Выход зависит от выбранного вами дистрибутива, поэтому, если мы решили пойти с std:: normal_distribution со значением 2
для среднего и stddev например dist(2, 2)
вместо этого вывод будет похож на этот (увидеть его в прямом эфире):
-6
-5
-4
-3
-2 **
-1 ****
0 *******
1 *********
2 *********
3 *******
4 ****
5 **
6
7
8
9
Ниже приведена модифицированная версия некоторого кода, представленного в N3551
(см. его в прямом эфире):
#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;
}
Результаты будут выглядеть так:
5H 5S AS 9S 4D 6H TH 6D KH 2S QS 9H 8H 3D KC TD 7H 2D KS 3C TC 7D 4C QH QC QD JD AH JC AC KD 9D 5C 2H 4H 9C 8C JH 5D 4S 7C AD 3S 8S TS 2C 8D 3H 6C JS 7S 6S
подталкивания
Конечно Boost.Random всегда является опцией, здесь я использую повышение:: случайный:: uniform_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()
Если вы должны использовать rand()
, тогда мы можем перейти к FAQ по C для руководства по Как я могу генерировать случайные числа с плавающей запятой?, в котором в основном приведен пример, подобный этому для генерации a на интервале [0,1)
:
#include <stdlib.h>
double randZeroToOne()
{
return rand() / (RAND_MAX + 1.);
}
и создать случайное число в диапазоне от [M,N)
:
double randMToN(double M, double N)
{
return M + (rand() / ( RAND_MAX / (N-M) ) ) ;
}
Взгляните на Boost.Random. Вы можете сделать что-то вроде этого:
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();
}
Играйте вокруг, вы можете лучше пропустить один и тот же объект mt19937, а не конструировать новый каждый раз, но, надеюсь, вы получите эту идею.
Вызвать код с двумя значениями float
, код работает в любом диапазоне.
float rand_FloatRange(float a, float b)
{
return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}
Если вы используете С++, а не C, помните, что в техническом отчете 1 (TR1) и в проекте С++ 0x они добавили возможности для генератора случайных чисел в заголовочном файле, я считаю, что он идентичен библиотека Boost.Random и определенно более гибкая и "современная", чем функция библиотеки C, rand.
Этот синтаксис предлагает возможность выбора генератора (например, mersenne twister mt19937), а затем выберите дистрибутив (обычный, bernoulli, binomial и т.д..).
Синтаксис выглядит следующим образом (бесстыдно заимствовано из этого сайта):
#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;
В современном c++
вы можете использовать заголовок <random>
, поставляемый с c++11
.
Для получения случайного числа с float
вы можете использовать std::uniform_real_distribution<>
.
Вы можете использовать функцию для генерации чисел, и если вы не хотите, чтобы числа были одинаковыми все время, установите движок и распределение static
.
Пример:
float get_random()
{
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
return dis(e);
}
Идеально поместить float
в контейнер, такой как 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 << " ";
}
Пример вывода:
0.0518757 0.969106 0.0985112 0.0895674 0.895542
В некоторых системах (в настоящее время Windows с VC приходит в голову), RAND_MAX
смехотворно мал, i. е. всего 15 бит. При делении на RAND_MAX
вы создаете мантисс из 15 бит вместо 23 возможных бит. Это может быть или не быть проблемой для вас, но в этом случае вы упускаете некоторые значения.
О, только что заметил, что уже есть комментарий к этой проблеме. Во всяком случае, здесь есть код, который может решить это для вас:
float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);
Неподтвержденный, но может работать: -)
drand48(3)
является стандартным способом POSIX. GLibC также предоставляет реентерабельную версию drand48_r(3)
.
Функция была объявлена устаревшей в SVID 3, но адекватная альтернатива не была предоставлена, поэтому IEEE Std 1003.1-2013 по-прежнему включает в себя и не имеет записей, которые он будет куда угодно в ближайшее время.
В Windows стандартный способ CryptGenRandom().
Я не был удовлетворен ни одним из ответов до сих пор, поэтому я написал новую функцию случайного float. Он делает побитовые предположения о типе данных float. Он по-прежнему нуждается в функции rand() с не менее 15 случайными битами.
//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));
}
По-моему, вышеприведенный ответ дает некоторый "случайный" float, но ни один из них не является по-настоящему случайным поплавком (т.е. они пропускают часть представления с плавающей точкой). Прежде чем я буду спешить с моей реализацией, сначала взглянем на стандартный формат ANSI/IEEE для float:
| знак (1-бит) | e (8 бит) | f (23-бит) |
число, представленное этим словом, равно (-1 * знак) * 2 ^ e * 1.f
Обратите внимание на то, что число "e" является предвзятым (с смещением 127), таким образом, варьируя от -127 до 126. Самая простая (и фактически самая случайная) функция заключается в том, чтобы просто записать данные случайного int в float, таким образом
int tmp = rand();
float f = (float)*((float*)&tmp);
обратите внимание, что если вы сделаете float f = (float)rand();
, он преобразует целое число в float (таким образом, 10 станет 10.0).
Итак, теперь, если вы хотите ограничить максимальное значение, вы можете сделать что-то вроде (не уверен, что это работает)
int tmp = rand();
float f = *((float*)&tmp);
tmp = (unsigned int)f // note float to int conversion!
tmp %= max_number;
f -= tmp;
но если вы посмотрите на структуру поплавка, вы увидите, что максимальное значение поплавка (приблизительно) 2 ^ 127, которое больше, чем максимальное значение int (2 ^ 32), тем самым исключая значительную часть чисел, которые могут быть представлены поплавком. Это моя окончательная реализация:
/**
* 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;
}
используя эту функцию randf(0, 8, 0)
, вернет случайное число между 0.0 и 255.0
Если вы знаете, что ваш формат с плавающей запятой IEEE 754 (почти все современные процессоры, включая Intel и ARM), тогда вы можете построить случайное плавающее номер точки из случайного целого с использованием битовых методов. Это следует учитывать только в том случае, если у вас нет доступа к С++ 11 random
или Boost.Random
, которые намного лучше.
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;
}
Это даст лучшее распределение, чем использование деления.
Для С++ он может генерировать реальные числа с плавающей запятой в пределах диапазона, указанного переменной dist
#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);
}
rand() возвращает int между 0 и RAND_MAX. Чтобы получить случайное число между 0.0 и 1.0, сначала введите int return by rand() в float, а затем разделите на RAND_MAX.
#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));
...
Я не мог опубликовать два ответа, так что вот второе решение. log2 случайные числа, массивный уклон в сторону 0,0f, но это действительно случайное смещение от 1,0f до 0,0f.
#include <cstdint>
#include <cstdlib>
#include <ctime>
using namespace std;
float getval () {
union UNION {
uint32_t i;
float f;
} r;
/* 3 because it 0011, the first bit is the float sign.
* Clearing the second bit eliminates values > 1.0f.
*/
r.i = (rand () & 0xffff) + ((rand () & 0x3fff) << 16);
return r.f;
}
int main ()
{
srand (time (NULL));
...
Полностью случайное действительное число с плавающей точкой генерируется следующим образом: Случайный знак, случайный показатель и случайная мантисса. Ниже приведен пример генерации случайных чисел из 0..MAXFLOAT с равномерным распределением:
static float frand(){
float f;
UINT32 *fi = (UINT32*)&f;
*fi = 0;
const int minBitsRandGives = (1<<15); // RAND_MAX is at least (1<<15)
UINT32 randExp = (rand()%254)+1; // Exponents are in range of [1..254]
UINT32 randMantissa = ((rand() % minBitsRandGives) << 8) | (rand()%256);
*fi = randMantissa | (randExp<<23); // Build a float with random exponent and random mantissa
return f;
}
Важное примечание: RAND_MAX по умолчанию равен 2 ^ 16 (в 32-битных системах), поэтому rand() может генерировать не более 15 случайных бит. Поскольку с плавающей запятой имеется всего 32 бита, мы должны активировать rand() не менее 3 раз для генерации случайных 32 бит. Я использовал 8 бит rand() для генерации Exponent и еще 2 обращения к rand() для генерации 23 бит мантиссы.
Общая ошибка:. Если вы используете (float)rand()/MAX_RAND
для получения плавающей запятой в диапазоне [0..1], вы все равно получите случайные числа в равномерном распределении, но с низким точность. Например, ваш случайный генератор может генерировать 0,00001 и 0,00002, но не может генерировать 0,000017. Такое случайное число в 256 раз меньше, чем фактическое представление с плавающей запятой.
Оптимизация: Моя функция не оптимизирована для скорости. Вы можете улучшить его, заменив разделение "%" на побитовые логические операции. Например, вместо %256
используйте &0xFF