Треки
/
Python
Python
/
Салабус
/
Випадковість
Ви

Випадковість у Python

{one: "1 вправа", few: "%{count} вправи", many: "%{count} вправ", other: "%{count} вправи"}

Про концепцію Випадковість

Багато програм потребують (начебто) випадкових значень, щоб імітувати події реального світу.

Поширені, знайомі приклади:

  • Підкидання монети: випадкове значення з ('Heads', 'Tails').
  • Кидання кубика: випадкове ціле число від 1 до 6.
  • Перемішування колоди карт: випадковий порядок списку карт.

Генерування справді випадкових значень на компʼютері є напрочуд складною технічною задачею, тож ці результати можуть називати «псевдовипадковими».

На практиці добре спроєктована бібліотека, як-от модуль random зі стандартної бібліотеки Python, швидка, гнучка й дає результати, цілком достатні для більшості застосунків у моделюванні, симуляції та іграх.

Далі на цій сторінці ми наведемо кілька найпоширеніших функцій з random. Радимо дослідити повну документацію random, адже там набагато більше можливостей, ніж ми тут розглядаємо.

Caution

Модуль random НЕ варто використовувати для застосунків, повʼязаних із безпекою чи криптографією.

Натомість Python надає модуль secrets. Він спеціально оптимізований для криптографічної безпеки. Деякі з попередніх проблем і причин створення модуля secrets можна знайти в PEP 506.

Імпортування

Перш ніж скористатися інструментами з модуля random, його потрібно спершу імпортувати:

>>> import random

# Choose random integer from a range
>>> random.randrange(1000)
360

>>> random.randrange(-1, 500)
228

>>> random.randrange(-10, 11, 2)
-8

# Choose random integer between two values (inclusive)
>>> random.randint(5, 25)
22

Щоб не набирати назву модуля щоразу, можна імпортувати конкретні функції за назвою:

>>> from random import choice, choices

# Using choice() to pick Heads or Tails 10 times
>>> tosses = []
... for side in range(10):
...    tosses.append(choice(['H', 'T']))    

>>> print(tosses)
['H', 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'H']


# Using choices() to pick Heads or Tails 8 times
>>> picks = []
... picks.extend(choices(['H', 'T'], k=8))
... print(picks)
['T', 'H', 'H', 'T', 'H', 'H', 'T', 'T']

Створення випадкових цілих чисел

Функція randrange() має три форми, щоб вибрати випадкове значення з range(start, stop, step):

  1. randrange(stop) дає ціле число n таке, що 0 <= n < stop
  2. randrange(start, stop) дає ціле число n таке, що start <= n < stop
  3. randrange(start, stop, step) дає ціле число n таке, що start <= n < stop, і n належить послідовності start, start + step, start + 2*step...

Для поширеного випадку, коли step == 1, функція randint(a, b) може бути зручнішою та зрозумілішою. Можливі результати randint() включають верхню межу, тож randint(a, b) дає те саме, що й randrange(a, b+1):

>>> import random

# Select one number at random from the range 0, 499
>>> random.randrange(500)
219

# Select 10 numbers at random between 0 and 9 two steps apart.
>>> numbers = []
... for integer in range(10):
...    numbers.append(random.randrange(0, 10, 2))

>>> print(numbers)
[2, 8, 4, 0, 4, 2, 6, 6, 8, 8]

# roll a die
>>> random.randint(1, 6)  
4

Робота з послідовностями

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

Зазвичай це list, або, з деякими обмеженнями, tuple чи set (tuple незмінний, а set невпорядкований).

choice() і choices()

Функція choice() повертає один елемент, вибраний випадково з заданої послідовності. У найпростішому випадку це може бути підкидання монети:

# This will pick one of the two values in the list at random 5 separate times 
>>> [random.choice(['H', 'T']) for _ in range(5)]
['T', 'H', 'H', 'T', 'H']

По суті того самого можна досягти за допомогою функції choices(), передавши ключовий аргумент із довжиною списку:

>>> random.choices(['H', 'T'], k=5)
['T', 'H', 'T', 'H', 'H']

У наведених вище прикладах ми припускали чесну монету з однаковою ймовірністю випадання орла чи решки, але можна також задати вагові коефіцієнти. Наприклад, якщо в торбині 10 червоних кульок і 15 зелених, і ми хочемо випадково витягнути одну:

>>> random.choices(['red', 'green'], [10, 15])
['red']

sample()

Наведений вище приклад із choices() припускає те, що статистики називають «вибіркою з поверненням». Кожен вибір не впливає на ймовірність наступних виборів, і розподіл можливих варіантів залишається однаковим від вибору до вибору.

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

Щоб змоделювати цю «вибірку без повернення», модуль random надає функцію sample(). Синтаксис sample() схожий на choices(), але додає ключовий параметр counts:

>>> random.sample(['red', 'green'], counts=[10, 15], k=10)
['green', 'green', 'green', 'green', 'green', 'red', 'red', 'red', 'red', 'green']

Вибірки повертаються в тому порядку, у якому їх було вибрано.

shuffle()

І choices(), і sample() повертають нові списки, коли k > 1. На противагу цьому, shuffle() перемішує порядок списку на місці, і початковий порядок втрачається:

>>> my_list = [1, 2, 3, 4, 5]
>>> random.shuffle(my_list)
>>> my_list
[4, 1, 5, 2, 3]

Робота з розподілами

Досі ми зосереджувалися на випадках, де всі результати рівноймовірні. Наприклад, random.randrange(100) з однаковою ймовірністю дає будь-яке ціле число від 0 до 99.

Багато ситуацій реального світу набагато складніші за це. Тож статистики створили безліч distributions, щоб математично описати результати «реального світу».

Рівномірні розподіли

Для цілих чисел randrange() і randint() використовують, коли всі ймовірності рівні. Такий розподіл називають uniform.

Існують аналоги randrange() і randint() для чисел з плаваючою комою.

random() дає значення float x таке, що 0.0 <= x < 1.0.

uniform(a, b) дає x таке, що a <= x <= b.

>>> [round(random.random(), 3) for _ in range(5)]
[0.876, 0.084, 0.483, 0.22, 0.863]

>>> [round(random.uniform(2, 5), 3) for _ in range(5)]
[2.798, 2.539, 3.779, 3.363, 4.33]

Розподіл Гауса

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

Наприклад, уявімо, що завод, де ми працюємо, щойно придбав 10 000 болтів, які мають бути однаковими. Ми хочемо налаштувати заводського робота, щоб він їх обробляв, тож зважуємо вибірку зі 100 штук і виявляємо, що їхня середня (або mean) вага становить 4,731 г. Це аж ніяк не означає, що всі вони важать рівно 4,731 г. Можливо, виявиться, що значення коливаються від 4,627 до 4,794 г, але скупчуються навколо 4,731 г.

Ідеться про Gaussian distribution, у якому ймовірності сягають піку біля середнього значення й симетрично спадають з обох боків (звідси «дзвоноподібна форма»). Щоб змоделювати це в програмі, нам потрібен якийсь спосіб задати ширину кривої (зазвичай дорогі болти скупчуються навколо середнього щільніше, ніж дешеві!).

За домовленістю, для цього використовують standard deviation: малі значення дають різку вузьку криву, а великі - низьку й широку. Математики люблять грецькі літери, тож ми використовуємо μ («мю») для позначення середнього й σ («сигма») для позначення стандартного відхилення. Отже, якщо ми читаємо, що «95% значень лежать у межах 2σ від μ» або «бозон Хіггса виявлено з 5-сигмальною достовірністю», такі твердження стосуються стандартного відхилення.

>>> mu = 4.731
>>> sigma = 0.316
>>> [round(random.gauss(mu, sigma), 3) for _ in range(5)]
[4.72, 4.957, 4.64, 4.556, 4.968]
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці