Почему C ++ rand () генерирует только числа одинакового порядка?

146

В небольшом приложении, написанном на C / C ++, я столкнулся с проблемой с randфункцией и, возможно, с семенем:

Я хочу создать последовательность случайных чисел, которые имеют разные порядки, то есть с различными значениями логарифма (основание 2). Но кажется, что все произведенные числа имеют один и тот же порядок, колеблющийся между 2 ^ 25 и 2 ^ 30.

Это потому, что rand()засеян со временем Unix, которое сейчас является относительно большим числом? Что я забыл? Я сею rand()только один раз в начале main().

Талларон Матиас
источник
7
FWIW так, это C или C ++? Если под C / C ++ вы имеете в виду, что вы действительно можете использовать C ++, и упоминание C было случайным, может быть, это поможет en.cppreference.com/w/cpp/numeric/random/binomial_distribution .
Р. Мартиньо Фернандес
9
К сожалению, вы сделали ставку не на ту лошадь. Семя не должно быть вашей проблемой. Ваша проблема была неправильно ожидаемого распределения. Поскольку непредвзятый программист будет ожидать rand()возвращения равномерно распределенных чисел (об этом явно говорится в документации с высоким рейтингом Google), я не думаю, что этот вопрос будет полезен для будущих читателей. Вот почему проголосуйте, но не позволяйте этому отговорить вас от использования SO.
Император Орионий
12
@ doug65536 "... где число никогда не повторяется" - это не случайно! Я мог бы выплатить пенсию за столом для крэпса, если бы моя игра rand () никогда не возвращала одно и то же число дважды, пока не было возвращено все возможное число.
Крис Грегг
6
@GalacticCowboy Не путайте периодичность с повторением отдельных чисел. Из статьи Википедии, на которую вы ссылались: «повторный результат не означает, что конец периода достигнут, поскольку его внутреннее состояние может быть больше, чем его выход». Было бы очень, очень плохо, если бы PRNG генерировал значение, а затем гарантированно не генерировал это значение снова, пока не были возвращены все значения.
Крис Грегг
12
Doug65536, никто не дрался. Они просто правильно заявляют, что вы не правы. Если бы я хотел RAND в диапазоне от 1 до 10, PRNG мог бы с радостью выпустить следующее: это было бы вполне допустимо, несмотря на кратные 2 и 7. Я думаю, что вы путаете PRNG с возможностью случайного воспроизведения на вашем iPhone.
Отдых на Кипре

Ответы:

479

Есть только 3% чисел между 1 и 2 30, которые НЕ между 2 25 и 2 30 . Итак, это звучит довольно нормально :)

Из - 2 25 /2 30 = 2 -5 = 1/32 = 0,03125 = 3,125%

C4stor
источник
36
Да, хорошая мысль! Число между 2 ^ 25 и 2 ^ 30 в 31 раз больше, чем между 1 и 2 ^ 25 :) спасибо за быстрый ответ. Мне нужно переосмыслить программу тогда. На вопрос ответил.
Талларон Матиас
1
@TallaronMathias Подумайте об обрезании числа с помощью >>сдвига битов - это даст вам меньшие числа. (Или принимая модуль с %.)
Шон Оллред
13
Я ожидаю, что это будет очевидным для большинства программистов: любое целое число без знака меньше 2 ^ 25 должно иметь первые 7 битов, 0и если каждый бит случайный ...
BlueRaja - Danny Pflughoeft
118
@ BlueRaja-DannyPflughoeft - если бы вероятности были очевидны, казино были бы вне бизнеса.
Бретт Хейл
26
@BrettHale - я не думаю, что программисты являются целевой аудиторией казино.
EkoostikMartin
272

Светло-зеленый - это область от 0 до 2 25 ; темно-зеленый - область между 2 25 и 2 30 . Клещи имеют степень 2.

распределение

Кейси Чу
источник
42

Вам нужно быть более точным: вам нужны разные значения логарифма по основанию 2, но какое распределение вы хотите для этого? Стандартные функции rand () генерируют равномерное распределение, вам нужно преобразовать этот вывод, используя функцию квантиля, связанную с желаемым распределением.

Если вы сообщите нам о дистрибутиве, то мы можем сообщить вам нужную вам quantileфункцию.

Вирсавия
источник
13
+1, распределение является ключевым термином. Не имеет смысла говорить о случайных числах, когда ничего не известно о распределении. Униформа - это особый случай, хотя и важный. Может быть, это хорошее место, чтобы указать на различные дистрибутивы из стандартной библиотеки C ++ 11.
оставил около
18

Если вы хотите разные порядки, почему бы просто не попробовать pow(2, rand())? Или, возможно, выбрать порядок непосредственно как rand (), как предложил Гарольд?

aspiring_sarge
источник
3
хорошая идея, но вы должны исправить свой ответ, используя pow вместо ^ (который является логическим оператором xor, а не power, на языке Си).
Крис
6
Так как rand()может доходить до RAND_MAX, вам действительно нужно масштабировать ваше случайное число, чтобы результат не переполнялся ...
Floris
@Floris: но если вы масштабируете небольшой счетный диапазон на очень большом диапазоне, у вас будет МНОГО дыр, что, вероятно, не то, что ожидает OP.
Андре Карон
13

@ C4stor сделал замечательную мысль. Но для более общего случая и более понятного для человека (база 10): для диапазона от 1 до 10 ^ n, ~ 90% чисел составляют от 10 ^ (n-1) до 10 ^ n, следовательно, ~ 99% чисел идут от 10 ^ (n-2) до 10 ^ n. Продолжайте добавлять столько десятичных знаков, сколько вы хотите.

Забавная математика, если вы продолжаете делать это для n, вы можете видеть, что с этим методом от 1 до 10 ^ n, 99,9999 ...% = 100% чисел от 10 ^ 0 до 10 ^ n.

Теперь о коде, если вы хотите случайное число со случайными порядками величин от 0 до 10 ^ n, вы можете сделать:

  1. Генерация небольшого случайного числа от 0 до n

  2. Если вы знаете диапазон, который имеет n, сгенерируйте большое случайное число порядка 10 ^ k, где k> max {n}.

  3. Вырежьте длинное случайное число, чтобы получить n цифр этого большого случайного числа.

Франциско Презенсия
источник
46
Вы совершенно правы, но для ДЕЙСТВИТЕЛЬНО легкого для понимания ответа ОП должен спросить себя, почему 90% случайных чисел от 1 до 100 являются двумя цифрами.
Спросите о Монике
13

Основной (и правильный) ответ уже был дан и принят выше: имеется 10 чисел от 0 до 9, 90 чисел от 10 до 99, 900 от 100 до 999 и т. Д.

Для эффективного с точки зрения вычислений способа получения распределения с приблизительно логарифмическим распределением вы хотите сдвинуть случайное число вправо на случайное число:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

Это не идеально, но это гораздо быстрее, чем вычисления pow(2, rand()*scalefactor). Это будет «комом» в том смысле, что распределение будет равномерным для чисел с коэффициентом 2 (равномерно для 128–255, половина плотности для 256–1023 и т. Д.).

Вот гистограмма частоты чисел от 0 до 31 (в 1М выборках):

введите описание изображения здесь

Floris
источник
Ниппик: это поощряет очень маленькие числа больше, чем можно было бы ожидать. Вероятность получить ноль значительно выше, чем 10.
Mooing Duck
Что ж, весь смысл в том, чтобы поощрять небольшие числа, поэтому я рад, что это работает! Я запустил симуляцию Монте-Карло, и это дает мне снижение вероятности в 2 раза, так как числа удваиваются - в отличие от распределения бревен. Обновленный ответ с картинкой.
Флорис
нет, я имею в виду, что rand()>>(rand()&31);можно было бы интуитивно ожидать, что 1/32 числа будет иметь 32 бита, а 1/32 числа будет иметь 31 бит, а 1/32 числа будет иметь 30 бит и т. д. Но это не результаты, которые вы получаете, только около 1/64 числа будет иметь 32 бита, а почти половина должна быть 0. Поскольку моя математическая математика не согласна с вашими измерениями, мне придется делать свои собственные измерения, чтобы вычислить это из
Mooing Duck
2
Я не хочу сказать, что ваш код неверен. Это, наверное, то, что я бы сделал. Это просто заслуживает предупреждения о том, что результаты не совсем распределены, как можно было бы ожидать.
Mooing Duck
1
Я думаю, что проблема заключается в представлении 0 как 1-битного числа ... это та головоломка, с которой вы сталкиваетесь, когда смешиваете целые числа и логарифмы. Это было хорошее упражнение, и вы дали мне кое-что подумать. «Проверь пределы своего алгоритма» - он никогда не стареет.
Флорис
5

Число от 0 до 2 ^ 29 и от 2 до 29 и от 2 до 30 точно совпадают.

Другой способ взглянуть на проблему: рассмотрите двоичное представление генерируемого вами случайного числа, вероятность того, что старший бит равен 1, равна 1/2, и, следовательно, вы получите порядок 29 в половине случаев. Вам нужно увидеть число, которое будет меньше 2 ^ 25, но это означает, что 5 старших битов равны нулю, что происходит с низкой вероятностью 1/32. Скорее всего, даже если вы запустите его в течение длительного времени, вы никогда не увидите порядок ниже 15 (вероятность - что-то вроде 6 6 раз подряд).

Теперь часть вашего вопроса о семени. Нет, начальное число не может определить диапазон, из которого генерируются числа, оно просто определяет первый, начальный элемент. Думайте о rand () как о последовательности всех возможных чисел в диапазоне (предопределенная перестановка). Семя определяет, где вы начнете рисовать числа из последовательности. Вот почему, если вы хотите (псевдо) случайности, вы используете текущее время для инициализации последовательности: вам все равно, что позиция, с которой вы начинаете, распределена неравномерно, все, что имеет значение, это то, что вы никогда не начинаете с одной и той же позиции.

Вадим
источник
2

Используйте pow(2,rand()) это даст ответы в порядке желаемой величины!

Shivendra
источник
2

Если вы хотите использовать случайные числа из онлайн-сервиса, для этого вы можете использовать wget, возможно, вы захотите использовать такие сервисы, как random.org, для генерации случайных чисел, вы можете поймать их, используя wget, и затем читать числа из загруженный файл

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html

Намит Синха
источник
Добро пожаловать в ТАК. пожалуйста, воздержитесь от публикации ссылок в качестве ответов. Вы можете предоставить подробный эскиз ответа, оставив подробности для чтения по ссылкам.
Шай