Случайные величины и перемешивание данных с помощью модуля random

Раздел: Основы Python -> Стандартная библиотека

Модуль random входит в стандартную библиотеку Python и предоставляет широкий набор функций для получения псевдослучайных чисел, выбора элементов из последовательностей, перемешивания коллекций, а также для работы с различными распределениями вероятностей. Основа работы модуля - генератор псевдослучайных чисел (PRNG) на алгоритме Mersenne Twister, который обеспечивает хорошую статистическую случайность для большинства прикладных задач.

Основное эффективное решение для типовых потребностей:

  • random.randint(a, b) - возвращает случайное целое число в интервале [a, b] (включая обе границы).
  • random.choice(seq) - возвращает один случайный элемент из непустой последовательности seq.
  • random.shuffle(seq) - перемешивает изменяемую последовательность (список) на месте, ничего не возвращает.
  • random.sample(population, k) - возвращает новый список из k уникальных элементов из population (без повторений).
  • random.random() - возвращает случайное число с плавающей запятой в интервале [0.0, 1.0).
  • random.uniform(a, b) - случайное число с плавающей запятой в интервале [a, b] (возможны границы).
import random

# Целое число от 1 до 6 (как бросок кубика)
dice = random.randint(1, 6)
print(f'Выпало: {dice}')

# Случайный элемент из списка
colors = ['красный', 'зеленый', 'синий']
chosen = random.choice(colors)
print(f'Выбран цвет: {chosen}')

# Перемешивание списка
cards = ['A', '2', '3', '4', '5']
random.shuffle(cards)
print('Перетасованные карты:', cards)

# Выбор трех уникальных чисел из диапазона
numbers = random.sample(range(10), 3)
print('Три случайных уникальных числа:', numbers)

# Случайное вещественное от 0 до 1
r = random.random()
print('Случайное число [0,1):', r)

# Вещественное в произвольном интервале
temp = random.uniform(36.0, 37.5)
print('Температура:', temp)

Python module site (модуль site в python)

Этот набор покрывает 90 процентов типовых сценариев. Если нужна воспроизводимость, используется random.seed(n) - одинаковое начальное значение дает одинаковую последовательность.

Варианты решений и их цели

Как сгенерировать случайное целое число без границы или с шагом?

Для генерации случайного целого с указанным шагом можно использовать random.randrange(start, stop, step). Эта функция работает аналогично range, но возвращает случайное значение из полученной последовательности. Если нужны очень большие целые числа (например, для криптографии), следует применять random.getrandbits(k), который возвращает k-битное неотрицательное целое число.

import random

# Четное число от 0 до 20 с шагом 2
even = random.randrange(0, 21, 2)
print('Случайное четное:', even)

# 16-битное число
big = random.getrandbits(16)
print('16-битное целое:', big)

стандартные библиотеки python (стандартные библиотеки python)

Типичная ошибка: Использование random.randint для получения случайного элемента из диапазона с шагом, отличным от 1, приводит к некорректным результатам, так как randint всегда использует шаг 1. Правильный путь - random.randrange или комбинация randint с умножением.

Решение: Если шаг не равен 1, применяйте randrange. Если нужен произвольный список целых чисел, используйте random.choice из заранее подготовленного списка.

Как получить случайное число с плавающей точкой, распределенное по нормальному закону?

В модуле random есть функции для различных распределений: random.gauss(mu, sigma) - нормальное распределение с заданным средним mu и стандартным отклонением sigma; random.betavariate(alpha, beta) - бета-распределение; random.expovariate(lambd) - экспоненциальное распределение; random.triangular(low, high, mode) - треугольное распределение.

import random

# Нормальное распределение (среднее 0, стд. откл. 1)
x = random.gauss(0, 1)
print('Значение из нормального распределения:', x)

# Треугольное распределение от 1 до 10 с модой 5
t = random.triangular(1, 10, 5)
print('Треугольное распределение:', t)

Python random py (использование модуля random)

Проблема: Использование random.gauss в многопоточных приложениях может привести к недетерминированному поведению, так как функция не является потокобезопасной. Для потокобезопасных задач лучше использовать random.Random с отдельными экземплярами или модуль secrets.

Решение: Создать отдельный экземпляр random.Random в каждом потоке: rand_gen = random.Random(). Для криптографической надежности применяется модуль secrets.

Как обеспечить воспроизводимость случайной последовательности?

Установка начального числа генератора через random.seed() гарантирует, что при каждом запуске (с тем же значением seed) будет генерироваться одна и та же последовательность чисел. Это важно для отладки, тестирования и демонстраций.

import random

random.seed(42)
print(random.randint(1, 100))  # Всегда 82
random.seed(42)
print(random.randint(1, 100))  # Снова 82

Ошибка: Вызов random.seed() без аргумента сбрасывает генератор на случайное значение (обычно из системного времени), что не дает воспроизводимости.

Решение: Для воспроизводимости всегда передавайте фиксированное число (целое или байтовую строку). Если нужно сбросить на случайное начальное состояние, используйте random.seed(None) или просто не вызывайте seed.

Как случайно перемешать список и вернуть новый список?

random.shuffle модифицирует исходный список на месте. Если требуется новая перемешанная копия, можно создать копию списка и перемешать её, либо использовать random.sample с длиной, равной исходной (для уникальных элементов).

import random

original = [1, 2, 3, 4, 5]

# Способ 1: копия и shuffle
shuffled_copy = original[:]
random.shuffle(shuffled_copy)
print('Оригинал:', original)
print('Перемешанная копия:', shuffled_copy)

# Способ 2: sample с k=len(original)
shuffled_sample = random.sample(original, len(original))
print('Через sample:', shuffled_sample)

Ошибка: Попытка вызвать random.shuffle на неизменяемом объекте (кортеже, строке) приводит к ошибке типа AttributeError: 'tuple' object has no attribute 'shuffle'.

Решение: Преобразовать кортеж в список, перемешать список, потом при необходимости обратно. Для строк можно использовать random.sample и затем ''.join.

Как сгенерировать случайный пароль или токен?

Для генерации случайных строк (пароли, ключи) рекомендуется использовать модуль secrets (криптостойкий), а не random, так как random не предназначен для безопасности. Однако в учебных целях можно использовать random.choice с набором символов.

import random
import string

symbols = string.ascii_letters + string.digits + '!@#$%^&*'
password_length = 12
password = ''.join(random.choice(symbols) for _ in range(password_length))
print('Сгенерированный пароль (не для продакшена):', password)

Проблема: Модуль random использует Mersenne Twister, который предсказуем при наличии достаточного количества выходных данных. Для криптографических целей это небезопасно.

Решение: Вместо random применяйте secrets.choice и secrets.token_bytes. Для учебных примеров random подходит.

Расширенные примеры использования модуля random

Далее приведены менее распространенные, но полезные сценарии, демонстрирующие гибкость модуля random.

1. Использование Random с пользовательским состоянием (экземпляры класса Random)

Модуль random предоставляет класс Random, который можно инстанцировать для создания независимых генераторов с собственными состояниями. Это полезно в многопоточных приложениях или когда нужно несколько независимых потоков случайности.

Пример
import random

gen1 = random.Random(123)
gen2 = random.Random(456)

# Оба генератора дают разные последовательности
print('gen1:', [gen1.randint(1, 100) for _ in range(5)])
print('gen2:', [gen2.randint(1, 100) for _ in range(5)])

Результат может быть, например:

gen1: [13, 86, 53, 76, 40]
gen2: [34, 1, 89, 37, 93]

Такой подход позволяет изолировать случайность в разных частях программы, не опасаясь взаимного влияния.

2. Генерация последовательности с заданным весом выборки

Функция random.choices (обратите внимание на множественное число) выбирает k элементов с возможностью повторения и поддержкой весов. Это мощный инструмент для моделирования дискретных распределений.

Пример
import random

# Элементы и их веса
elements = ['камень', 'ножницы', 'бумага']
weights = [5, 3, 2]  # камень выпадает чаще всего
# Выберем 10 раз с возвращением
choices = random.choices(elements, weights=weights, k=10)
print('Выборка с весами:', choices)
# Подсчитаем частоты
from collections import Counter
print('Частоты:', Counter(choices))

Пример вывода:

Выборка с весами: ['камень', 'ножницы', 'камень', 'камень', 'бумага', 'камень', 'камень', 'бумага', 'ножницы', 'камень']
Частоты: Counter({'камень': 6, 'бумага': 2, 'ножницы': 2})

Параметр cum_weights позволяет задать накопленные веса для более быстрой обработки.

3. Случайное вращение (циклический сдвиг) с помощью random.randint и срезов

Иногда требуется не просто перемешать список, а выполнить случайный циклический сдвиг (rotate). Модуль random не имеет прямой функции, но это реализуется комбинацией срезов.

Пример
import random

def rotate_random(lst):
    if not lst:
        return lst
    k = random.randint(0, len(lst)-1)
    return lst[k:] + lst[:k]

original = [1, 2, 3, 4, 5, 6]
rotated = rotate_random(original)
print('Исходный:', original)
print('Сдвинутый:', rotated)

Результат (один из возможных):

Исходный: [1, 2, 3, 4, 5, 6]
Сдвинутый: [3, 4, 5, 6, 1, 2]

4. Использование random с системным энтропийным источником (SystemRandom)

Класс random.SystemRandom использует операционную систему для получения криптографически стойких случайных чисел (например, /dev/urandom в Linux). Его можно применять, когда нужна безопасная случайность, но без дополнительных зависимостей.

Пример
import random

sys_rand = random.SystemRandom()
# Теперь sys_rand имеет те же методы, что и обычный random
print('Безопасное случайное целое:', sys_rand.randint(1, 100))
print('Безопасный выбор:', sys_rand.choice(['A', 'B', 'C']))
# Однако shuffle и sample тоже работают
lst = [1, 2, 3, 4, 5]
sys_rand.shuffle(lst)
print('Безопасно перемешанный:', lst)

SystemRandom не поддерживает seed, всегда использует системную энтропию.

5. Моделирование броуновского движения (Wiener process) с помощью random.gauss

Комбинируя случайные числа из нормального распределения, можно смоделировать простейший случайный процесс. Код ниже генерирует 100 шагов броуновского движения.

Пример
import random
import matplotlib.pyplot as plt

# Одно из приложений, не требующее matplotlib в статье, просто демонстрация
random.seed(42)
n = 100
path = [0.0]
for _ in range(n):
    step = random.gauss(0, 0.1)  # стандартное отклонение 0.1
    path.append(path[-1] + step)

print('Первые 10 точек траектории:', path[:10])
# Для визуализации можно построить график
# plt.plot(path)
# plt.show()

Пример вывода:

Первые 10 точек траектории: [0.0, 0.014294915180924382, -0.02856444018174323, ...]

Этот подход используется в финансовой инженерии, физике и симуляциях.

6. Создание случайного подмножества с сохранением порядка

Функция random.sample возвращает элементы в случайном порядке. Если нужно выбрать k элементов из последовательности, но сохранить их исходный относительный порядок, можно воспользоваться следующим трюком:

Пример
import random

def sample_ordered(population, k):
    # Создаем список пар (индекс, элемент), выбираем случайные индексы и сортируем
    indices = list(range(len(population)))
    chosen_indices = set(random.sample(indices, k))
    return [population[i] for i in sorted(chosen_indices)]

letters = 'abcdefghij'
result = sample_ordered(letters, 4)
print('Выборка с сохранением порядка:', ''.join(result))

Пример:

Выборка с сохранением порядка: b d g j

(порядок символов соответствует исходному)

7. Использование random для создания случайного графа (модель Эрдеша-Реньи)

С помощью random.random можно создавать случайные графы, где каждое ребро появляется с вероятностью p.

Пример
import random

def generate_random_graph(n, p):
    """Генерирует матрицу смежности случайного графа с n вершинами, вероятность ребра p."""
    adj = [[False] * n for _ in range(n)]
    for i in range(n):
        for j in range(i+1, n):
            if random.random() < p:
                adj[i][j] = adj[j][i] = True
    return adj

random.seed(0)
graph = generate_random_graph(5, 0.4)
for row in graph:
    print([int(x) for x in row])

Результат:

[0, 1, 0, 0, 1]
[1, 0, 0, 0, 1]
[0, 0, 0, 0, 1]
[0, 0, 0, 0, 0]
[1, 1, 1, 0, 0]

Такая модель используется в теории сетей и для тестирования алгоритмов.

Использование модуля random - comments

En
Python random py (python)