Випадкове взагалі ледве випадкове?


79

Я зробив це для перевірки випадковості випадків:

>>> from random import randint
>>>
>>> uniques = []
>>> for i in range(4500):  # You can see I was optimistic.
...     x = randint(500, 5000)
...     if x in uniques:
...         raise Exception('We duped %d at iteration number %d' % (x, i))
...     uniques.append(x)
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
Exception: We duped 887 at iteration number 7

Я спробував приблизно в 10 разів більше, і найкращий результат, який я отримав, - 121 ітерація перед ретранслятором. Це найкращий результат, який ви можете отримати зі стандартної бібліотеки?


56
"Прагматичний програміст", правило 26. "виберіть" Не порушено. Рідко можна знайти помилку в ОС або компіляторі, або навіть сторонній продукт чи бібліотеку. Помилка, швидше за все, у додатку. Або в цьому випадку застосування теорії ймовірностей.

11
Просто обман: uniques = set () та uniques.add (x) були б більш доречними (ефективними).
Eric O Lebigot

22
Однією з ключових властивостей парадоксу дня народження є те, що він протиінтуїтивний. Якщо ви не знаєте про це або не маєте певного досвіду в теорії ймовірностей, тоді у вас не обов'язково буде будь-яка причина для пошуку за ключовим словом. Один із сайтів запитань та відповідей USP полягає в тому, що ви можете задати запитання такими словами, які насправді ніколи б не відповідали відповідям на запитання, якщо ви здійснили чистий пошук за ключовими словами, не знаючи, що шукати.
ConcernedOfTunbridgeWells

7
@okoku: (щодо вашої відповіді ConcernedOfTunbridge): те, про що ви говорите, - це зовсім інша проблема. Один - це ймовірність отримати одну і ту ж карту двічі поспіль; інший - це ймовірність отримати БУДЬ-ЯК з попередніх карт N-1 після N вибору. Середнє кількість карт з більш досконалої ГСЧ для другого завдання повинна бути близько 67; враховуючи, що у вас є десь від 8 до 121, це звучить приблизно так.
BlueRaja - Danny Pflughoeft

5
ви плутаєте Random з рівномірно розподіленим. Для генератора випадкових випадків цілком справедливо повертати одне і те ж значення багато разів. Якщо вам потрібен рівномірно розподілений генератор випадкових чисел, який є зовсім іншою проблемою, це проблема перетасовки, а не проблема генератора.

Відповіді:


287

Парадокс до дня народження, або те, чому PRNG продукують дублікати частіше, ніж ви думаєте.


У проблемі ОП є кілька питань. Один - парадокс дня народження, як згадувалося вище, а другий - характер того, що ви створюєте, що за своєю суттю не гарантує, що дане число не повториться.

Парадокс дня народження застосовується там, де дане значення може траплятися більше одного разу протягом періоду генератора - і, отже, дублювання може відбуватися в межах вибірки значень. Ефект Парадоксу до дня народження полягає в тому, що реальна ймовірність отримання таких дублікатів досить значна, і середній період між ними менший, ніж можна було б подумати. Цей дисонанс між сприйнятою та реальною ймовірністю робить Парадокс до дня народження хорошим прикладом когнітивного упередження , де наївна інтуїтивна оцінка, ймовірно, буде дико неправильною.

Швидкий посібник з генераторів випадкових чисел псевдо (PRNG)

Перша частина вашої проблеми полягає в тому, що ви приймаєте значення експонента генератора випадкових чисел і перетворюєте його в набагато менше число, тому простір можливих значень зменшується. Хоча деякі генератори псевдовипадкових чисел не повторюють значень протягом свого періоду, це перетворення змінює домен на значно менший. Менший домен анулює умову "заборони повторення", тому ви можете очікувати значної ймовірності повторень.

Деякі алгоритми, такі як лінійний конгруентний PRNG ( A'=AX|M) , гарантують унікальність протягом усього періоду. У LCG сформоване значення містить весь стан акумулятора, і додатковий стан не зберігається. Генератор детермінований і не може повторити число протягом періоду - будь-яке задане значення накопичувача може означати лише одне можливе послідовне значення. Отже, кожне значення може відбуватися лише один раз протягом періоду генератора. Однак період такого PRNG порівняно невеликий - близько 2 ^ 30 для типових реалізацій алгоритму LCG - і не може бути більшим за кількість різних значень.

Не всі алгоритми PRNG поділяють цю характеристику; деякі можуть повторити задане значення протягом періоду. У задачі OP алгоритм Мерсенна Твістера (використовуваний у випадковому модулі Python ) має дуже довгий період - набагато більший за 2 ^ 32. На відміну від лінійного конгруентного PRNG, результат не є чисто функцією попереднього вихідного значення, оскільки накопичувач містить додатковий стан. При 32-бітному цілочисельному виведенні та періоді ~ 2 ^ 19937 він не може надати таку гарантію.

Mersenne Twister - популярний алгоритм для PRNG, оскільки він має хороші статистичні та геометричні властивості та дуже тривалий період - бажані характеристики для PRNG, що використовується на імітаційних моделях.

  • Хороші статистичні властивості означають, що числа, породжені алгоритмом, розподіляються рівномірно, при цьому жодні числа не мають значно більшої ймовірності появи, ніж інші. Погані статистичні властивості можуть призвести до небажаних перекосів у результатах.

  • Хороші геометричні властивості означають, що набори з N чисел не лежать на гіперплощині в N-мірному просторі. Погані геометричні властивості можуть генерувати помилкові кореляції в імітаційній моделі та спотворювати результати.

  • Довгий період означає, що ви можете генерувати багато чисел до того, як послідовність обернеться до початку. Якщо модель потребує великої кількості ітерацій або її потрібно запускати з кількох насінин, тоді 2 ^ 30 або близько того дискретних чисел, доступних у типовій реалізації LCG, може бути недостатньо. Алгоритм MT19337 має дуже довгий період - 2 ^ 19337-1, або приблизно 10 ^ 5821. Для порівняння загальна кількість атомів у Всесвіті оцінюється приблизно в 10 ^ 80.

32-бітове ціле число, створене PRNG MT19337, не може представляти достатньо дискретних значень, щоб уникнути повторення протягом такого великого періоду. У цьому випадку повторювані значення можуть виникнути і неминучі при достатньо великій вибірці.

Парадокс до дня народження в двох словах

Ця проблема спочатку визначається як ймовірність того, що будь-які дві людини в кімнаті матимуть один день народження. Ключовим моментом є те, що будь-які двоє людей у ​​кімнаті могли б поділити день народження. Люди, як правило, наївно неправильно трактують проблему як ймовірність того, що хтось із присутніх проводить день народження з конкретною людиною, що є джерелом когнітивних упереджень, які часто змушують людей недооцінювати ймовірність. Це неправильне припущення - немає вимоги, щоб збіг ставився до конкретної особи, і будь-які дві особи могли б збігатися.

Цей графік показує ймовірність спільного дня народження із збільшенням кількості людей у ​​кімнаті.  Для 23 людей вірогідність двох спільних днів народження становить трохи більше 50%.

Імовірність збігу між будь-якими двома особами набагато вища, ніж ймовірність збігу з конкретною особою, оскільки збіг не повинен бути до певної дати. Швидше, вам доведеться знайти лише двох осіб, які мають однаковий день народження. З цього графіку (який можна знайти на сторінці Вікіпедії з цього приводу) ми бачимо, що нам потрібно лише 23 людини в кімнаті, щоб було 50% шансів знайти двох, які збігаються таким чином.

Зі статті Вікіпедії на цю тему ми можемо отримати гарне резюме. У проблемі OP ми маємо 4500 можливих «днів народження», а не 365. Для заданої кількості випадкових значень, що генеруються (що дорівнює «людям»), ми хочемо знати ймовірність появи будь-яких двох однакових значень у послідовності.

Обчислення ймовірного впливу Парадоксу до дня народження на проблему ОП

Для послідовності із 100 чисел ми маємо (100 * 99) / 2 = 4950 пари (див. Розуміння проблеми ), які потенційно можуть збігатися (тобто перше може збігатися з другим, третім тощо, друге може збігатися з третім, четвертим тощо тощо), тому кількість комбінацій, які потенційно можуть збігатися, перевищує лише 100.

З обчислення ймовірності ми отримуємо вираз 1 - (4500! / (4500 ** 100 * (4500 - 100)!) . Наступний фрагмент коду Python нижче наївно оцінює ймовірність виникнення пари, що відповідає.

# === birthday.py ===========================================
#
from math import log10, factorial

PV=4500          # Number of possible values
SS=100           # Sample size

# These intermediate results are exceedingly large numbers;
# Python automatically starts using bignums behind the scenes.
#
numerator = factorial (PV)          
denominator = (PV ** SS) * factorial (PV - SS)

# Now we need to get from bignums to floats without intermediate
# values too large to cast into a double.  Taking the logs and 
# subtracting them is equivalent to division.
#  
log_prob_no_pair = log10 (numerator) - log10 (denominator)

# We've just calculated the log of the probability that *NO*
# two matching pairs occur in the sample.  The probability
# of at least one collision is 1.0 - the probability that no 
# matching pairs exist.
#
print 1.0 - (10 ** log_prob_no_pair)

Це дає розумний результат p = 0,669 для збігу, що відбувається в межах 100 чисел, відібраних із сукупності 4500 можливих значень. (Можливо, хтось міг це перевірити та залишити коментар, якщо це неправильно). З цього ми бачимо, що довжина пробігів між відповідними числами, що спостерігаються OP, здається цілком розумною.

Виноска: використання перетасовки для отримання унікальної послідовності псевдовипадкових чисел

Дивіться цю відповідь нижче від С. Марка щодо засобів отримання гарантованого унікального набору випадкових чисел. Техніка, на яку посилається плакат, приймає масив цифр (які ви надаєте, щоб ви могли зробити їх унікальними) і перемішує їх у випадковий порядок. Малювання чисел послідовно з перетасованого масиву дасть вам послідовність псевдовипадкових чисел, які гарантовано не повторяться.

Виноска: Криптографічно захищені PRNG

Алгоритм МТ не є криптографічно безпечним, оскільки порівняно легко зробити висновок про внутрішній стан генератора, спостерігаючи послідовність чисел. Інші алгоритми, такі як Blum Blum Shub , використовуються для криптографічних програм, але можуть бути непридатними для моделювання або загальних додаткових випадкових чисел. Криптографічно захищені PRNG можуть бути дорогими (можливо, вимагатимуть обчислень бінгума) або можуть не мати хороших геометричних властивостей. У випадку з цим типом алгоритму основна вимога полягає в тому, що обчислювально неможливо зробити висновок про внутрішній стан генератора, спостерігаючи послідовність значень.


Одна корекція: PRNG на основі LCG, які використовуються належним чином, не гарантують унікальних результатів для повного циклу. Наприклад, традиційний турбопаскаль LCG має (IIRC) 31 біт внутрішнього стану, але він генерує лише 15-бітові числа, які можуть повторюватись і повторюватися протягом одного циклу.
Porculus

46

Перш ніж звинувачувати Python, вам слід реально розібратися з деякими теоріями ймовірностей та статистики. Почніть з прочитання про парадокс дня народження

До речі, randomмодуль на Python використовує твістер Мерсенна PRNG, який вважається дуже хорошим, має величезний період і був ретельно випробуваний. Тож будьте впевнені, що ви у добрих руках.


42

Якщо ви не хочете повторюваного, створіть послідовний масив і використовуйте random.shuffle


3
Бог я люблю random.shuffle. Це одне з стрижнів мого проекту :)
PizzAzzra


15

Справжня випадковість однозначно включає повторення значень до вичерпання всього набору можливих значень. Інакше це не було б випадковим випадком, оскільки ви могли б передбачити, як довго значення не буде повторюватися.

Якщо ви коли-небудь кидали кубики, то напевно досить часто отримували 3 шістки поспіль ...



4

Це не ретранслятор. Повторювач - це коли ви повторюєте ту саму послідовність . Не просто одне число.


4

Ви генеруєте 4500випадкові числа з діапазону 500 <= x <= 5000. Потім ви перевіряєте, чи не було воно для кожного номера раніше створеного. Проблема з днем народження повідомляє нам, яка ймовірність того, що два з цих чисел збігатимуться під час nспроб поза діапазоном d.

Ви також можете інвертувати формулу, щоб підрахувати, скільки чисел вам потрібно створити, доки шанс створити дублікат не перевищує 50%. У цьому випадку у вас є >50%шанс знайти повторюваний номер після 79ітерацій.


1

Ви визначили випадковий простір у 4501 значення (500-5000) і повторюєте 4500 разів. В основному вам гарантовано зіткнення в тесті, який ви написали.

Подумати про це по-іншому:

  • Коли масив результатів порожній P (дуп) = 0
  • 1 значення в масиві P (дуп) = 1/4500
  • 2 значення в масиві P (дуп) = 2/4500
  • тощо

Отже, до того моменту, як ви дійдете до 45/4500, ця вставка має 1% шансів бути дублікатом, і ця ймовірність постійно збільшується з кожною наступною вставкою.

Щоб створити тест, який справді перевіряє здібності випадкової функції, збільште всесвіт можливих випадкових значень (наприклад: 500-500000). Ви можете отримати або не отримати обману. Але в середньому ви отримаєте набагато більше ітерацій.


3
Ваша математика неправильна через проблему з днем ​​народження. Дивіться інші відповіді. Після 45 вставок у вас є 1% шансів повторити першу вставку, але у вас також є 44 інші чіткі вставки, які ви могли б повторити.
jcdyer

0

Для всіх, хто має цю проблему, я використовував uuid.uuid4 (), і це працює як шарм.


3
Тоді запитання, можливо, було б краще сформулювати так: "Я хочу сформувати серію неповторюваних чисел, randint () Python, здається, не робить цього - що робить?" а не "Генератор випадкових чисел Python поганий" :-) Припускаючи, що uuid4 () справді випадковий, він все одно може повторитися - просто дуже малоймовірно. Які фактичні властивості ви хочете від цифр? Не повторюється? Випадково? (Виберіть одного.) Не повторювати-часто? (Використовуйте більший діапазон INT, ефективно все uuid4, це здається.) Що саме ви хочете використовувати числа для реального питання.
agnoster

@agnoster Я справді не мав на меті образити Python, але Random: Відсутність передбачуваності, без будь-якого систематичного шаблону, та повторюваний шаблон: шаблон групи елементів, який повторюється знову і знову. Дивіться, генератор випадкових випадків не є випадковим, якщо він повторюється, оскільки тоді він має шаблон.
orokusaki

9
Ваше визначення поняття "випадковий" помилкове. Серйозно, поверніться і перечитайте фрагменти парадоксу дня народження. Справді генератор випадкових чисел все одно матиме повтори набагато частіше, ніж ви очікуєте за інтуїцією. Як зазначає @ConcernedOfTunbridgeW, ймовірність отримати повторення в діапазоні 500-5000 в межах перших 100 чисел становить ~ 66%, я вважаю, це зовсім не суперечить тому, що ви спостерігали. Випадковість не означає "без повторів", це просто означає ... ну, випадкова. Насправді, якщо ви гарантуєте відсутність повторів, генератор повинен бути менш випадковим, щоб забезпечити це.
agnoster

1
Питання про те, для чого вам потрібні ці цифри, все ще стоїть. Якщо ви спеціально хочете не повторювані числа, чому? uuid4 () (якщо це справді випадково) нічим не відрізняється від randint () з дуже великим діапазоном. Якщо ви хочете, щоб послідовність було важко вгадати, усунення повторень насправді шкодить вам, тому що коли я бачу число, скажімо, 33, я знаю, що в наступному не буде 33. Отже, примусове неповторення насправді робить вашу послідовність більш передбачуваною - бачите?
agnoster

0

Існує парадокс дня народження. Беручи це до уваги, ви усвідомлюєте, що ви говорите, що знаходження "764, 3875, 4290, 4378, 764" або щось подібне є не дуже випадковим, оскільки число в цій послідовності повторюється. Справжній спосіб це зробити - порівняти послідовності між собою. Для цього я написав сценарій python.

from random import randint
y = 21533456
uniques = []
for i in range(y):  
    x1 = str(randint(500, 5000))
    x2 = str(randint(500, 5000))
    x3 = str(randint(500, 5000))
    x4 = str(randint(500, 5000))
    x = (x1 + ", " + x2 + ", " + x3 + ", " + x4)
if x in uniques:
    raise Exception('We duped the sequence %d at iteration number %d' % (x, i))
else:
    raise Exception('Couldn\'t find a repeating sequence in %d iterations' % (y))
uniques.append(x)

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