Случайные числа в языке Python: от простых randint до сложных распределений

Раздел: Математика -> Генерация случайных чисел

Основные подходы к генерации случайных чисел

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

Пример базового получения случайного целого числа в интервале от 1 до 10:

import random
number = random.randint(1, 10)
print(number)

Import random python (импорт модуля random в python)

Функция randint(a, b) возвращает целое число N, где a <= N <= b. Границы включены.

Для чисел с плавающей точкой используется random.random(), которая даёт значение в полуинтервале [0.0, 1.0).

import random
x = random.random()
print(x)

Python случайное число (случайное число в python)

Если требуется случайное число из диапазона [a, b) с плавающей точкой, применяется uniform(a, b).

import random
y = random.uniform(2.5, 5.5)
print(y)

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

Функция randrange(start, stop[, step]) работает аналогично range, но возвращает случайное значение из последовательности.

import random
# случайное нечётное число от 1 до 9
odd = random.randrange(1, 10, 2)
print(odd)

Эта функция полезна, когда нужно избежать лишнего кода с перебором.

Типичная ошибка: путать порядок аргументов в randint и randrange. В randint границы включены, в randrange - stop не включается. Если написать random.randrange(1,10) - получим числа от 1 до 9.

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

Функция choice(seq) возвращает один случайный элемент из непустой последовательности.

import random
fruits = ["яблоко", "банан", "вишня", "дыня"]
selected = random.choice(fruits)
print("Выпало:", selected)

Аналогично для строки:

char = random.choice("ABCDEF")
print(char)

Случаи использования: случайный выбор вопроса из теста, генерация случайного символа.

Проблема: если последовательность пуста, возникает IndexError. Перед вызовом choice стоит проверять длину.

Как перемешать список на месте?

Метод shuffle(list) изменяет порядок элементов исходного списка случайным образом.

import random
deck = [2, 3, 4, 5, 6, 7, 8, 9, 10, "Валет", "Дама", "Король", "Туз"]
random.shuffle(deck)
print(deck)

Важно: shuffle модифицирует переданный список, а не возвращает новый. Если нужен новый порядок без изменения оригинала, сначала следует скопировать список: shuffled = original[:]; random.shuffle(shuffled).

Типичная ошибка: ожидать возврата нового списка от shuffle. Начинающие часто пишут new_list = random.shuffle(list) и получают None.

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

Функция sample(population, k) возвращает список длиной k из уникальных элементов популяции.

import random
numbers = range(100)
# выбрать 5 случайных чисел без повторений
group = random.sample(numbers, 5)
print(group)

Если k больше длины population, возникает ValueError.

Цель: создание случайных подмножеств, выбор победителей в лотерее.

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

Функция seed(a=None) инициализирует генератор случайных чисел. Если задать одинаковое значение, последовательность случайных чисел будет повторяться.

import random
random.seed(42)
print([random.randint(1,6) for _ in range(5)])

# Повторный запуск даст тот же результат
random.seed(42)
print([random.randint(1,6) for _ in range(5)])

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

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

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

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

import secrets
# случайное целое в диапазоне [0, 100)
secure_int = secrets.randbelow(100)
print(secure_int)

# произвольный токен из 32 байт в шестнадцатеричном виде
token = secrets.token_hex(32)
print(token)

Функция randbelow(n) даёт равномерно распределённое целое от 0 до n-1 с криптографической безопасностью.

Распространённая ошибка: использовать random.randint для генерации паролей. Это небезопасно, так как последовательность Mersenne Twister может быть восстановлена за 624 значений.

Как генерировать числа из нормального распределения?

Модуль random включает функции для других распределений. gauss(mu, sigma) возвращает число из нормального распределения с заданным средним и стандартным отклонением.

import random
mu, sigma = 0, 0.1
sample = [random.gauss(mu, sigma) for _ in range(1000)]
# среднее и стд. откл. на выборке
print("Среднее:", sum(sample)/len(sample))
print("Стд.откл.:", (sum((x - sum(sample)/len(sample))**2 for x in sample)/(len(sample)-1))**0.5)

Доступны также expovariate (экспоненциальное), betavariate (бета-распределение) и другие. Они применяются в моделировании и статистике.

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

Для научных и инженерных расчётов удобен модуль numpy.random. Он предоставляет быстрые векторизованные операции и множество распределений.

import numpy as np
# массив 3x2 случайных чисел из равномерного распределения [0,1)
arr_uni = np.random.random((3,2))
print(arr_uni)

# массив из нормального распределения N(0,1) размером (100,)
norm_arr = np.random.normal(0, 1, 100)
print(norm_arr.shape)

NumPy также поддерживает seed через numpy.random.seed() или более современный объект Generator.

Проблема совместимости: при параллельных вычислениях с процессами seed, заданный в одном процессе, не влияет на другие. Для воспроизводимости в multiprocessing seed необходимо явно передавать каждому процессу.

Расширенные примеры генерации случайных значений

1. Генерация случайной строки с заданными символами (cryptographically strong)

Используется модуль secrets и метод token_hex для токенов, но если требуется строка из определённого набора символов, можно комбинировать choice:

Пример
import secrets
import string

def random_string(length):
    alphabet = string.ascii_letters + string.digits
    return ''.join(secrets.choice(alphabet) for _ in range(length))

password = random_string(16)
print(password)
R7gH2kL9qW4xYz1E

Пояснение: string.ascii_letters содержит 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', string.digits - '0123456789'. Секретный выбор каждого символа гарантирует отсутствие предсказуемости.

2. Восстановление seed после серии случайных чисел (только для демонстрации)

Теоретически Mersenne Twister можно взломать. Пример показывает, как, зная 624 последовательных вызова random.getrandbits(32), можно восстановить состояние. На практике это делается с помощью пакета randcrack, но здесь приведён фрагмент для иллюстрации:

Пример
import random
random.seed(12345)
# соберём первые 624 целых числа
state = []
for _ in range(624):
    state.append(random.getrandbits(32))
# С помощью этих данных можно реконструировать seed с помощью алгоритма Mersenne Twister
print("Собрано", len(state), "значений")
Собрано 624 значений

Пояснение: Это показывает уязвимость random для криптографии. Для настоящего восстановления требуется реверс-инжиниринг или готовая библиотека.

3. Выборка с весами (weighted random)

Модуль random имеет функцию choices, позволяющую задать веса для каждого элемента:

Пример
import random
items = ['камень', 'ножницы', 'бумага']
weights = [0.5, 0.3, 0.2]  # камень с вероятностью 50%
result = random.choices(items, weights=weights, k=10)
print(result)
# подсчёт частот
from collections import Counter
print(Counter(result))
['камень', 'камень', 'ножницы', 'камень', 'бумага', 'камень', 'ножницы', 'камень', 'бумага', 'ножницы']
Counter({'камень': 5, 'ножницы': 3, 'бумага': 2})

Пояснение: weights могут не суммироваться в 1; они нормируются автоматически. Параметр k - количество выбранных элементов.

4. Генерация случайных чисел из распределения Пуассона с помощью SciPy

Для более сложных распределений используют SciPy (статистические функции). Пример получения случайной величины Пуассона:

Пример
import numpy as np
from scipy.stats import poisson

mu = 5.0          # среднее
sample = poisson.rvs(mu, size=10)
print(sample)
# среднее выборки
print("Среднее выборки:", np.mean(sample))
[4 3 6 5 4 4 7 5 3 5]
Среднее выборки: 4.6

Пояснение: poisson.rvs генерирует массив заданного размера из распределения Пуассона с интенсивностью mu. Подходит для моделирования числа событий за интервал времени.

5. Использование numpy.random.Generator (новый стиль)

В NumPy 1.17+ рекомендуется использовать объекты Generator, а не функции напрямую. Это даёт большую гибкость и разные алгоритмы.

Пример
import numpy as np

rng = np.random.default_rng(seed=2024)  # задаём seed
# случайные числа из равномерного [0,1)
uniform = rng.random(5)
print(uniform)

# случайные целые от 0 до 100
ints = rng.integers(0, 100, size=10)
print(ints)

# нормальное распределение
norm = rng.normal(loc=10, scale=2, size=3)
print(norm)
[0.28595934 0.39874537 0.97131079 0.07214612 0.26691343]
[26 78  7 60 62 91 74 93 20 41]
[11.24361462  8.99242675  9.86032531]

Пояснение: Generator поддерживает множество алгоритмов (PCG64, SFC64, MT19937). По умолчанию используется PCG64 - быстрый и качественный.

6. Генерация случайных точек на сфере (равномерно)

Для имитации случайных направлений в трёхмерном пространстве нужны точки на единичной сфере. Используется метод Марсальи:

Пример
import numpy as np

def random_points_on_sphere(n):
    # нормальное распределение на 3 координаты
    vec = np.random.normal(0, 1, (n, 3))
    # нормировка на единичную длину
    norm = np.linalg.norm(vec, axis=1, keepdims=True)
    return vec / norm

points = random_points_on_sphere(5)
print(points)
# проверка суммы квадратов
print(np.sum(points**2, axis=1))
[[-0.51278592 -0.49337553  0.70242965]
 [ 0.11706411 -0.38526859  0.91532245]
 [ 0.57990455  0.63188068  0.51463304]
 [ 0.43794024 -0.67081663  0.598327  ]
 [-0.60238382 -0.27331235 -0.7491622 ]]
[1. 1. 1. 1. 1.]

Пояснение: Используется свойство, что нормально распределённые векторы равномерно распределены по направлению. Этот метод не требует отклонения точек и эффективен.

Случайное число в Python - comments

En
Python случайное число (python)