Случайные числа в Python: модуль random с примерами

Случайные числа в Python: модуль random с примерами Полезное

Модуль random — это встроенный модуль стандартной библиотеки Python для генерации псевдослучайных чисел: целых и вещественных, а также случайного выбора и перемешивания элементов последовательностей. Он не требует установки и подключается строкой import random.

Разберу основные функции модуля (randint, random, uniform, choice, shuffle, sample) с воспроизводимыми примерами, покажу, как работает seed и почему одинаковый seed дает одинаковый результат, отдельно отмечу границу безопасности (для паролей и токенов нужен модуль secrets, а не random) и коротко покажу numpy для массивов.

Минимальный рабочий пример

Вот полный скрипт, который бросает игральный кубик три раза. Его можно скопировать и запустить как есть:

import random

random.seed(1)                 # фиксирую генератор, чтобы вывод повторялся
for i in range(3):
    brosok = random.randint(1, 6)
    print("Бросок", i + 1, "->", brosok)

Вывод:

Бросок 1 -> 2
Бросок 2 -> 5
Бросок 3 -> 1

Здесь random.randint(1, 6) возвращает случайное целое от 1 до 6 включительно, а random.seed(1) в начале делает результат воспроизводимым: при каждом запуске выпадет та же тройка. Уберите строку с seed — и числа будут меняться от запуска к запуску. Почему так — в разделе про псевдослучайность.

Основные функции модуля random

Модуль покрывает три задачи: сгенерировать число, выбрать элемент из набора и перемешать набор. Ниже — сводка самых используемых функций, а под таблицей — один общий пример с фактическим выводом.

Функция Что делает Пример вызова
random() вещественное в промежутке [0.0, 1.0) random.random()
randint(a, b) целое от a до b включительно random.randint(1, 6)
randrange(a, b) целое от a до b, но b не включается random.randrange(1, 6)
uniform(a, b) вещественное от a до b random.uniform(10, 20)
choice(seq) случайный элемент последовательности random.choice(menu)
choices(seq, k=n) n элементов с повторами, можно с весами random.choices(menu, k=3)
sample(seq, k=n) n разных элементов без повторов random.sample(nums, 6)
shuffle(seq) перемешивает список на месте random.shuffle(deck)

Общий пример под одним фиксированным seed — вывод воспроизводится дословно:

import random

random.seed(42)                     # фиксирую генератор -> вывод воспроизводим

print(random.random())              # вещественное в [0.0, 1.0)
print(random.randint(1, 6))         # целое от 1 до 6 включительно
print(random.uniform(10, 20))       # вещественное в [10, 20]
print(random.choice(["орел", "решка"]))
print(random.sample(range(1, 50), 6))
print(random.choices(["A", "B", "C"], weights=[5, 3, 2], k=4))
deck = ["туз", "король", "дама", "валет"]
random.shuffle(deck)
print(deck)

Вывод:

0.6394267984578837
1
17.41550499759833
орел
[15, 9, 7, 44, 35, 6]
['B', 'A', 'A', 'A']
['король', 'валет', 'дама', 'туз']

Различайте близкие вещи: random() дает дробь в [0.0, 1.0) без аргументов, uniform(a, b) — дробь в заданном промежутке. randint(a, b) включает верхнюю границу, а randrange(a, b) — нет (как срез списка). sample возвращает элементы без повторов, choices — с повторами и с необязательными весами. shuffle меняет список на месте и возвращает None — к этой типичной ошибке вернусь ниже.

Псевдослучайность и seed

Числа из модуля random не по-настоящему случайны, а псевдослучайны: их выдает детерминированный алгоритм (генератор Mersenne Twister), который по одному начальному числу — seed — разворачивает длинную последовательность, статистически похожую на случайную. Один и тот же seed всегда дает одну и ту же последовательность.

Если seed не задать явно, Python берет его из системного источника (по умолчанию — из данных ОС или текущего времени), поэтому при обычном запуске числа разные. Фиксированный seed нужен, когда результат важно повторить: тесты, отладка, воспроизводимые эксперименты, учебные примеры.

import random

random.seed(42)
run1 = [random.randint(1, 100) for _ in range(5)]

random.seed(42)                     # тот же seed -> та же последовательность
run2 = [random.randint(1, 100) for _ in range(5)]

print(run1)
print(run2)
print(run1 == run2)

Вывод:

[82, 15, 4, 95, 36]
[82, 15, 4, 95, 36]
True

Важная оговорка: seed делает вывод повторяемым, но не «более случайным». Он не улучшает качество чисел — только фиксирует стартовую точку.

random не годится для паролей и токенов

Здесь проходит граница безопасности. Генератор random предсказуем: зная состояние или несколько подряд идущих значений, злоумышленник может восстановить остальные. Для генерации паролей, токенов, ключей, идентификаторов сессий и любых секретов используйте модуль secrets — он опирается на криптостойкий источник ОС.

import secrets

# случайный токен: 32 шестнадцатеричных символа
print(secrets.token_hex(16))

# случайный пароль из букв и цифр
import string
alphabet = string.ascii_letters + string.digits
password = "".join(secrets.choice(alphabet) for _ in range(12))
print(password)

Вывод здесь намеренно не привожу: он должен быть разным при каждом запуске (в этом и смысл). У secrets есть аналоги привычных функций: secrets.choice, secrets.randbelow(n), secrets.token_hex, secrets.token_urlsafe. Правило простое: развлечения, симуляции, перемешивание, игры — random; все, что защищает данные, — secrets.

numpy.random для массивов

Когда нужны не отдельные числа, а сразу массивы (машинное обучение, численные расчеты), удобнее сторонняя библиотека numpy — она генерирует целые массивы за один вызов и работает быстрее цикла на чистом Python. Современный способ — создать генератор через default_rng и передать ему seed:

import numpy as np

rng = np.random.default_rng(42)     # свой seed, независимый от random
print(rng.integers(1, 7, size=5))   # 5 целых в диапазоне [1, 7)
print(rng.random(3))                # массив из 3 дробей в [0.0, 1.0)

Вывод:

[1 5 4 3 3]
[0.69736803 0.09417735 0.97562235]

Обратите внимание: numpy использует собственный генератор со своим seed, поэтому random.seed() на него не влияет и наоборот. Диапазон integers(1, 7) не включает 7 — как range и randrange.

Три частые ошибки

Ошибка 1: присвоить результат shuffle. Метод shuffle перемешивает список на месте и возвращает None, а не новый список.

import random
deck = [1, 2, 3, 4, 5]
result = random.shuffle(deck)
print(result)     # None - а ждали перемешанный список

Вывод: None, при этом сам deck уже перемешан. Исправление — перемешать на месте, а список читать по имени; если нужен новый список без изменения исходного, взять sample:

random.shuffle(deck)          # deck меняется на месте
new = random.sample(deck, len(deck))   # или так: копия без мутации

Ошибка 2: перепутать границы randint и randrange. randint(1, 3) может вернуть 3, а randrange(1, 3) — нет (верхняя граница исключается).

import random
random.seed(0)
print([random.randint(1, 3) for _ in range(8)])     # 3 встречается
random.seed(0)
print([random.randrange(1, 3) for _ in range(8)])   # только 1 и 2

Вывод:

[2, 2, 1, 2, 3, 2, 2, 2]
[2, 2, 1, 2, 2, 2, 2, 2]

Ошибка 3: запросить у sample больше элементов, чем есть. sample берет элементы без повторов, поэтому k не может превышать длину последовательности.

import random
random.sample([1, 2, 3], 5)

Вывод — исключение, а не «сколько получится»:

ValueError: Sample larger than population or is negative

Исправление — уменьшить k до размера набора либо взять choices, который допускает повторы: random.choices([1, 2, 3], k=5).

Выводы

  • Модуль random встроен в Python и подключается через import random; установка не нужна.
  • Базовые функции: random() и uniform() для дробей, randint() и randrange() для целых, choice()/choices()/sample() для выбора, shuffle() для перемешивания.
  • Числа псевдослучайны: одинаковый seed дает одинаковую последовательность, что удобно для тестов и воспроизводимых примеров, но не делает числа «более случайными».
  • Для паролей, токенов и любых секретов random не подходит — он предсказуем; берите модуль secrets.
  • Для массивов чисел используйте numpy с собственным генератором default_rng(seed); он независим от random.

Где применяется / связь с практикой

Случайные числа нужны в играх (бросок кубика, раздача карт, поведение врагов), в симуляциях и методе Монте-Карло, в перемешивании выборок и разбиении данных на train/test, в инициализации весов нейросетей, в A/B-тестах и в генерации тестовых данных. Как только в задаче звучит «случайно выбрать», «перемешать» или «сгенерировать пример», за этим обычно стоит random.

Освойте тему на практике

Освоить Python с нуля, включая работу с числами, коллекциями и модулями стандартной библиотеки, можно на курсе python-basic в Otus. Посмотреть формат и уровень занятий, не оплачивая курс, помогут бесплатные вебинары — там разбирают конкретные задачи и отвечают на вопросы.

Смежные темы: Python и создание 2D-игры: что нужно знать разработчику, Как создать нейросеть с нуля: рабочий пример на Python.

FAQ

Как получить одно и то же случайное число каждый раз?
Задайте seed перед генерацией: random.seed(42). Тот же seed дает ту же последовательность вызовов. Чтобы снова получить исходный результат, сбросьте seed перед повторным блоком.

Как выбрать случайный элемент словаря?
Ключи и значения словаря напрямую в random.choice не передать — нужен список: random.choice(list(mydict)) вернет случайный ключ, а random.choice(list(mydict.values())) — случайное значение.

Чем random.choices отличается от random.sample?
sample возвращает разные элементы без повторов и падает, если запросить больше, чем есть. choices возвращает элементы с повторами (один и тот же может выпасть дважды) и поддерживает веса через аргумент weights.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)