Чому на 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 прямо говорить про це), я не думаю, що це питання корисне для майбутніх читачів. Ось чому голосуйте проти, але не дозволяйте це відштовхувати вас від використання ТА.
Імператор Оріоній

12
@ doug65536 "... де жодне число ніколи не повторюється" - це не випадково! Я міг би фінансувати свою пенсію за столом крейдів, якщо мої кубики rand () ніколи не повертали одне і те ж число двічі, поки не було повернено кожне можливе число.
Кріс Грегг

6
@GalacticCowboy Не помиляйтеся періодичністю з повторенням окремих чисел. З цитованої вами статті у Вікіпедії: "повторний результат не означає, що кінець періоду досягнуто, оскільки його внутрішній стан може бути більшим, ніж його вихід". Було б дуже, дуже погано, якби PRNG створив значення, а потім гарантується, що більше не буде виробляти це значення до повернення всіх значень.
Кріс Грегг

12
Doug65536, ніхто не веде бійки. Вони просто правильно заявляють, що ви неправі. PRNG міг би із задоволенням викрити наступне, якби я хотів, щоб RAND між 1 та 10: 2 4 7 2 8 1 5 9 7 3 Це було б цілком дійсно, незважаючи на кілька 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%


36
Так, хороший пункт! Існує в 31 раз більше цифр між 2 ^ 25 і 2 ^ 30, ніж між 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 (), як запропонував Гарольд?


3
гарна ідея, але ви повинні виправити свою відповідь, використовуючи pow замість ^ (що є логічним оператором xor, а не power, мовою C).
kriss

6
Оскільки rand()можна піднятися RAND_MAX, вам дійсно потрібно масштабувати випадкове число, щоб результат не переповнюється ...
Флоріс,

@Floris: але якщо масштабувати невеликий обчислювальний діапазон на дуже великому діапазоні, у вас буде багато отворів, що, мабуть, не те, що очікує ОП.
Андре Карон

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М):

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


Нітпік: це заохочує дуже малу кількість більше, ніж можна було очікувати. Ймовірність отримати нуль значно вище, ніж 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()) це дасть відповіді в порядку бажаної величини !!


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


Ласкаво просимо до SO. будь ласка, утримуйтесь від розміщення посилань як відповідей. Ви можете надати детальний ескіз відповіді, залишаючи деталі для читання за посиланнями.
Шай
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.