Представьте, что вы пишете игру: враги должны появляться в неожиданных местах, а из сундука — выпадать случайный предмет. Или собираете тест, где вопросы должны каждый раз перемешиваться. Всё это — задачи на случайность, и в Python за неё отвечает встроенный модуль random. Устанавливать его не нужно, он уже есть в стандартной библиотеке — достаточно написать import random, как вы уже делали с другими модулями.
Дробное случайное число: random() и uniform()
Простыми словами. Представьте рулетку, у которой на ободе бесконечно много делений от нуля до единицы — крутанули, и стрелка остановилась где-то между ними. Иногда программе нужно именно такое «где-то между» — не целое число, а дробное, с плавающей точкой.
Термин. За это отвечает функция random.random() — она не принимает аргументов и всегда возвращает float из диапазона [0.0, 1.0): ноль включён, единица — нет. Если нужен диапазон пошире, есть random.uniform(a, b), где оба конца включены.
import random
x = random.random()
print(x)
y = random.uniform(1.5, 4.5)
print(y)
import random— подключаем модуль, без этой строки имяrandomвнутри кода не существует.x = random.random()— вызываем функциюrandomиз модуляrandom(да, они называются одинаково: слева от точки — имя модуля, справа — имя функции внутри него) и сохраняем дробное число в переменнуюx.print(x)— напечатает что-то вроде0.374448871...: при каждом запуске программы число будет другим.y = random.uniform(1.5, 4.5)— просим дробное число от 1.5 до 4.5, оба конца возможны.
Зачем нужен именно интервал от 0 до 1? Это удобная «единица измерения» вероятности. Например, событие с шансом 30% можно смоделировать так: if random.random() < 0.3: — условие сработает примерно в 30% запусков, потому что ровно 30% всех чисел на отрезке [0.0, 1.0) меньше 0.3.
Случайное целое число: randint() и randrange()
Простыми словами. Это игральный кубик: бросили — получили целое число от 1 до 6, без дробей и без вариантов вроде 3.7. В программах такое нужно постоянно: случайный уровень сложности, случайный индекс, случайная позиция на поле.
Термин. random.randint(a, b) возвращает случайное целое число из диапазона от a до b включительно — в отличие от привычного вам range(), здесь второе число тоже может выпасть. Если нужна более гибкая настройка — шаг или диапазон, не включающий правую границу, — есть random.randrange(start, stop, step), она ведёт себя как range(), только возвращает не последовательность, а одно случайное число из неё.
import random
dice = random.randint(1, 6)
print(dice)
even = random.randrange(0, 10, 2)
print(even)
dice = random.randint(1, 6)— просим целое число от 1 до 6, оба конца возможны: это ровно грани кубика.even = random.randrange(0, 10, 2)— строим воображаемую последовательностьrange(0, 10, 2), то есть 0, 2, 4, 6, 8, и берём из неё одно случайное значение; 10 в неё не входит, как и в обычномrange().
Не перепутайте границы. random.randint(1, 6) может вернуть и 1, и 6. А вот random.randrange(1, 6) — уже нет, верхняя граница здесь не включается, совсем как в range(). Одна буква в названии функции, а поведение на краю диапазона разное.
Случайный выбор из вариантов: choice() и choices()
Простыми словами. Представьте шляпу с бумажками, на каждой — своё имя, и вы наугад тянете одну. Или ту же бумажку кладут обратно и тянут снова, поэтому одно и то же имя может выпасть несколько раз подряд.
Термин. random.choice(последовательность) вытаскивает один случайный элемент из списка, кортежа или строки. random.choices(последовательность, k=n) — более сильный вариант: возвращает список из n элементов, выбранных с повторениями (после каждого выбора «бумажка» кладётся обратно), а необязательный параметр weights позволяет сделать одни варианты вероятнее других.
import random
players = ["Аня", "Боря", "Вика"]
first = random.choice(players)
print(first)
rolls = random.choices([1, 2, 3, 4, 5, 6], k=3)
print(rolls)
first = random.choice(players)— берёт ровно один элемент из спискаplayersи кладёт его в переменную; сам списокplayersпри этом не меняется.rolls = random.choices([1, 2, 3, 4, 5, 6], k=3)— три раза подряд выбирает случайное число из списка граней кубика; поскольку выбор идёт с повторениями, результат может выглядеть как[4, 4, 2]— четвёрка вполне может выпасть дважды.
Где эта разница важна. choice() хорош, когда нужен один результат: случайный вопрос теста, случайный противник. choices() — когда нужно много значений сразу и повторы допустимы: набор символов для пароля, серия бросков кубика, случайные оценки для теста программы.
Без повторов: sample() и shuffle()
Простыми словами. Раздать карты из колоды — это выбор без повторов: одна и та же карта не может попасть в руки дважды. А перетасовать колоду — это оставить те же карты, но перемешать их порядок.
Термин. random.sample(последовательность, k=n) возвращает новый список из n случайных элементов, выбранных без повторений — исходная коллекция не меняется. random.shuffle(список) работает иначе: она не возвращает новый список, а перемешивает элементы прямо внутри переданного списка и отдаёт None.
import random
deck = ["6", "7", "8", "9", "10", "J", "Q", "K"]
hand = random.sample(deck, k=3)
print(hand)
print(deck)
random.shuffle(deck)
print(deck)
hand = random.sample(deck, k=3)— забирает 3 случайные карты без повторов и складывает их в новый списокhand.print(deck)после этого напечатает исходную колоду без изменений —sample()её не трогает, а только читает.random.shuffle(deck)— перемешивает сам списокdeckна месте; строку видаdeck = random.shuffle(deck)писать не нужно и нельзя: результат вызова —None, вы потеряете колоду.
Частая ловушка. Написать deck = random.shuffle(deck) и получить None вместо перемешанной колоды. Правильно — вызвать random.shuffle(deck) отдельной строкой, без присваивания: список изменится сам, а обращаться к нему нужно по прежнему имени deck.
Повторяемая случайность: random.seed()
Простыми словами. Кажется странным словосочетание «повторяемая случайность», но представьте генератор случайных чисел как очень длинный список чисел, записанный заранее. Если каждый раз начинать читать этот список с одной и той же страницы, вы будете получать одну и ту же последовательность — хотя числа в ней выглядят совершенно случайными.
Термин. На самом деле компьютер не умеет быть по-настоящему случайным: random использует генератор псевдослучайных чисел (сокращённо ГПСЧ) — алгоритм, который по стартовому числу вычисляет длинную последовательность значений, выглядящих беспорядочно. Это стартовое число называется seed (по-русски — «зерно», «затравка»), и задать его вручную можно функцией random.seed(число). Если вызвать её перед серией случайных вызовов, результат при каждом запуске программы будет одинаковым — это удобно для тестов и для проверки решений на этой платформе.
import random
random.seed(42)
print(random.randint(1, 100))
print(random.randint(1, 100))
random.seed(42)
print(random.randint(1, 100))
print(random.randint(1, 100))
random.seed(42)— задаёт стартовую точку генератора; число 42 здесь ничем не особенное, подошло бы любое.- Первые два
print(random.randint(1, 100))напечатают какие-то два числа, например 82 и 15. - После повторного
random.seed(42)генератор снова стартует с той же точки, поэтому следующие дваprint()напечатают ровно 82 и 15 ещё раз — в том же порядке.
Совет. Без random.seed() генератор запускается от текущего момента времени, и последовательность каждый раз новая — это и есть «настоящая» случайность для игры или теста. А seed() нужен, когда важна воспроизводимость: например, чтобы проверяющая система на платформе могла заранее знать, какой результат ожидать от вашей функции.
Частые ошибки
Вызвать choice() или sample() на пустой коллекции. Тянуть бумажку из пустой шляпы не получится — Python сообщит об этом ошибкой:
import random
options = []
pick = random.choice(options)
# IndexError: Cannot choose from an empty sequence
Попросить у sample() больше элементов, чем есть в коллекции. Без повторов трижды одно и то же не выбрать:
import random
colors = ["red", "green", "blue"]
picked = random.sample(colors, k=5)
# ValueError: Sample larger than population or is negative
Сохранить результат shuffle() в переменную. Функция ничего не возвращает, она меняет список на месте:
import random
numbers = [1, 2, 3, 4, 5]
numbers = random.shuffle(numbers)
print(numbers) # None, список потерян
Что важно запомнить
import random— модуль встроенный, устанавливать ничего не нужноrandom.random()— дробное число из [0.0, 1.0);random.uniform(a, b)— дробное из [a, b]random.randint(a, b)— целое число, оба конца включены;random.randrange(start, stop, step)— как range(), но одно случайное значениеrandom.choice(seq)— один случайный элемент;random.choices(seq, k=n)— n элементов с повторениямиrandom.sample(seq, k=n)— n элементов без повторений, возвращает новый список, исходный не трогаетrandom.shuffle(lst)— перемешивает список на месте и возвращает None, присваивать результат не нужноrandom.seed(число)— фиксирует стартовую точку генератора, чтобы последовательность повторялась- Это генератор псевдослучайных чисел: для игр и тестов достаточно, для паролей и защиты данных используют модуль
secrets
Проверьте себя
7 вопросов
Симулятор броска кубиков
Напишите функцию roll_dice(), которая симулирует бросок двух шестигранных кубиков. Функция должна возвращать кортеж из трёх значений: (значение_первого_кубика, значение_второго_кубика, сумма).
Используйте random.randint(1, 6) для генерации каждого значения: сначала первого кубика, затем второго.