Rand() возвращает только половину из возможных значений, как бороться?

107
28 декабря 2020, 23:50

Функция rand() возвращает псевдослучайное число от 0 до 2^31 -1. Она никогда не возвращает отрицательные числа, но при этом "ворует" аж половину диапазона возможных значений, ведь могла бы отдавать uint32_t, а не положительную половину int32_t. Как это исправить? И как запросить просто несколько псевдослучайных байт, но не "урезанных" на половину? Меня интересуют именно библиотеки, которые можно было бы подключить через include и не переписывать код отдельно под windows/linux. Следующий код показывает, что при использовании знаковых переменных первый бит "урезается". Почему так?

std::random_device RD;
std::uniform_int_distribution<int64_t> id;
std::uniform_int_distribution<uint64_t> uid;
std::cout << id.min() << std::endl;
std::cout << id.max() << std::endl;
std::cout << uid.min() << std::endl;
std::cout << uid.max() << std::endl;

Выхлоп:

0
9223372036854775807
0
18446744073709551615
Answer 1

Функция rand() возвращает псевдослучайное число в диапазоне от 0 до RAND_MAX. Чему равно RAND_MAX - зависит от реализации. Из того, что в используемой вами реализации это значение равно 231-1 совсем не значит, что везде так.

Другими словами функция rand() генерирует [log2(RAND_MAX+1)] псевдослучайных битов. Вызвав эту функцию несколько раз, вы можете получить сколько угодно псевдослучайных битов и "состыковать" эти биты в число любой разрядности.

Например, на вашей платформе

uint32_t r = ((uint32_t) RAND_MAX + 1) * rand() + rand();

даст вам псевдослучайное число в диапазоне uint32_t.

Это, однако, несколько расточительно, ибо мы используем не все сгенерированные псевдослучайные биты. Не составит труда написать более общий вариант, который будет использовать rand() для генерации непрерывного потока псевдослучайных битов, и затем "выкусывать" из него кусочки требуемой длины - хоть байтов, хоть килобайтов.

В любом случае, если вас в рамках вашей задачи удовлетворяют характеристики псевдослучайных чисел, генерируемых простейшими генераторами, то реализация функции типа rand() занимает одну-две строчки. Реализуйте себе свою, под требуемый вам диапазон.

Answer 2

Функция rand() возвращает псевдослучайное число по РАВНОМЕРНОМУ закону распределения случайных чисел. Равномерное распределение чем характеризуется? Тем, что в среднем, по закону больших чисел, эти числа буду стремится к определенному "среднему" значению. rand() - всего лишь реализация закона распределения (и то, всего лишь псевдослучайная реализация). А не могли бы вы уточнить вопрос. Потому что спрашиваете про rand(), но почему-то функцию эту в коде не используете. Вместо этого пользуетесь классом uniform_int_distribution. При этому почему-то не пользуетесь возможностью этого класса: задавать минимальное и максимальное значения возможных случайных(!) чисел. Например

std::random_device rd;  /*используется для получения начального числа для генератора случайных чисел*/
std::mt19937 gen(rd()); //стандартный mersenne_twister_engine с начальным числом rd()
std::uniform_int_distribution<> dis(1, 6); /*тут мы устанавливаем минимальное и максимально возможные числа, т.е. диапазон случайных чисел*/
for (int n=0; n<10; ++n)
    /* можно использовать dis для преобразования случайного unsigned int, сгенерированного с помощью gen в int в диапазоне [1, 6] */
    std::cout << dis(gen) << ' ';

Собственно в предыдущем ответе практически тоже самое, но в виде функции.

"Поэтому я не могу понять, почему для знаковых чисел min() установлен в 0, а не в минимальное значение типа данных." - потому что это реализация равномерного закона распределения. Посмотрите стандарный график такого закона распределения - и вам все сразу же станет ясно.

Еще пример

#include <iostream>
#include <random> 
#include <ctime>
#include <string>
int main()
{ 
    std::mt19937 gen(time(0)); 
    std::uniform_int_distribution<int> uid1(0, 50), uid2(uid1.param()); 
    std::cout << "uid1 max: " << uid1.max() << std::endl 
              << "uid1 min: " << uid1.min() << std::endl 
              << "uid2 max: " << uid2.max() << std::endl 
              << "uid2 min: " << uid2.min() << std::endl;  
}

На экране появится:

uid1 max: 50
uid1 min: 0
uid2 max: 50
uid2 min: 0
Answer 3

Можете попробовать этот код, только с указанием нужных Вам типов.
Похоже, что это должно решить Вашу проблему:

double random(double from, double to)
{
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_real_distribution<>urd(from, to);
    return urd(mt);
}
READ ALSO
Функция copy и set

Функция copy и set

Мне нужно переместить list в set, я думал что для этого можно использовать функцию copy, но все оказалось не так просто, так как у класса set нету метода...

127
Qt5: QSqlDatabase::close()

Qt5: QSqlDatabase::close()

Как правильно закрыть за собой соединение с базой данных?

153
Как понять что ввод начался? с++

Как понять что ввод начался? с++

Есть два потока, когда начинается ввод в одном требуется остановить другой поток, как понять что ввод в первом потоке началсяЕсли использовать:

116
Дублирование чисел в wstring

Дублирование чисел в wstring

Необходимо продублировать все числа в строке, помогите исправить баг с бесконечным добавлением первого найденного числа

111