Модуль numpy.random

Модуль numpy.random отвечает за генерацию случайных чисел и выборок из различных распределений. Начиная с NumPy 1.17 рекомендуемый способ работы — через np.random.default_rng(): он возвращает объект Generator с современным алгоритмом PCG64 и явным управлением состоянием.

Старый интерфейс (np.random.rand, np.random.randint и т.п.) всё ещё работает, но в новом коде лучше пользоваться Generator.

Зачем нужно

  • Тестирование алгоритмов. Сгенерировать «шумные» данные, имитирующие измерения с датчиков.

  • Машинное обучение. Случайно перемешать выборку, инициализировать веса, разделить train/test.

  • Симуляция. Метод Монте-Карло, моделирование радио-помех, разброса параметров компонентов.

  • Игры и тесты. Случайные команды для робота, проверка устойчивости системы к шуму.

Пример 1. Создание генератора с seed

Передача seed делает результаты воспроизводимыми — это важно для отладки и публикаций.

import numpy as np

rng = np.random.default_rng(seed=42)
print(rng.random(3))

rng2 = np.random.default_rng(seed=42)
print(rng2.random(3))  # те же числа
[0.77395605 0.43887844 0.85859792]
[0.77395605 0.43887844 0.85859792]

Если seed не передан — генератор инициализируется системной энтропией, и результаты будут разными при каждом запуске.

Пример 2. Равномерное распределение

random() возвращает числа в [0, 1). Для произвольного диапазона — uniform.

import numpy as np

rng = np.random.default_rng(0)
pwm = rng.uniform(0, 255, size=5)   # имитация ШИМ
print(np.round(pwm, 1))
[161.7  39.1 213.8 215.9 168.4]

Пример 3. Целые числа

integers(low, high, size) — целые из полуинтервала [low, high). Если нужно включить границу — endpoint=True.

import numpy as np

rng = np.random.default_rng(1)
dice = rng.integers(1, 7, size=10)   # игральная кость
pins = rng.integers(2, 14, size=4)   # случайные пины Arduino
print(dice)
print(pins)
[5 6 1 1 2 6 1 4 6 2]
[10  7  3  8]

Пример 4. Нормальное распределение

normal(loc, scale, size) — гауссово распределение со средним loc и стандартным отклонением scale. Хороший шум для имитации погрешности датчика.

import numpy as np

rng = np.random.default_rng(2)
true_temp = 23.5
noisy = rng.normal(loc=true_temp, scale=0.2, size=8)
print(np.round(noisy, 2))
print("mean:", np.round(noisy.mean(), 3))
[23.42 23.41 23.6  23.62 23.35 23.16 23.5  23.78]
mean: 23.48

Пример 5. Случайный выбор (choice)

Выборка элементов из массива — с возвращением или без, с весами.

import numpy as np

rng = np.random.default_rng(3)
commands = np.array(['forward', 'back', 'left', 'right', 'stop'])

# 5 случайных команд с возвращением
seq = rng.choice(commands, size=5)
print(seq)

# 3 уникальных команды (без возврата)
uniq = rng.choice(commands, size=3, replace=False)
print(uniq)

# С весами — чаще "forward"
weighted = rng.choice(commands, size=6,
                      p=[0.5, 0.1, 0.15, 0.15, 0.1])
print(weighted)
['stop' 'forward' 'left' 'forward' 'back']
['right' 'stop' 'back']
['forward' 'forward' 'right' 'forward' 'stop' 'left']

Пример 6. Перемешивание (shuffle и permutation)

shuffle перемешивает на месте, permutation возвращает копию.

import numpy as np

rng = np.random.default_rng(4)
data = np.arange(10)

shuffled = rng.permutation(data)
print(shuffled)
print(data)            # исходный массив не тронут

rng.shuffle(data)
print(data)            # теперь перемешан на месте
[9 5 0 8 7 3 4 1 2 6]
[0 1 2 3 4 5 6 7 8 9]
[3 0 6 4 5 7 9 8 1 2]

Пример 7. Имитация шумного сигнала с датчика

Соберём «измерения» с дрейфом, шумом и редкими выбросами.

import numpy as np

rng = np.random.default_rng(5)
t = np.arange(20)

drift = 0.05 * t                                 # медленный дрейф
noise = rng.normal(0, 0.3, size=20)              # обычный шум
spikes = rng.choice([0, 5], size=20, p=[0.9, 0.1])  # 10% выбросов

signal = 25.0 + drift + noise + spikes
print(np.round(signal[:10], 2))
[25.   25.31 24.91 30.55 25.41 25.66 30.51 25.91 30.6  26.04]

Пример 8. Многомерные массивы

Любая функция принимает size в виде кортежа — получится массив нужной формы.

import numpy as np

rng = np.random.default_rng(6)
matrix = rng.normal(0, 1, size=(3, 4))
print(np.round(matrix, 2))
[[ 0.94 -1.32  0.65  0.65]
 [-0.42 -1.42 -0.42 -0.34]
 [ 0.36 -0.45 -0.4   0.4 ]]

Подводные камни

  • Старый vs новый API. np.random.rand и np.random.seed — устаревший глобальный интерфейс. В новом коде используйте rng = np.random.default_rng(seed) и методы на нём.

  • Несравнимые seed-ы. Один и тот же seed в старом и новом API даст разные последовательности — это нормально.

  • Воспроизводимость в потоках. Не делите один Generator между потоками. Используйте rng.spawn(n) для дочерних генераторов.

  • integers vs randint. В новом API метод называется integers, и по умолчанию верхняя граница НЕ включается, как и в старом randint — но не путайте с random_integers (устаревшая, включала границу).

  • Не используйте для криптографии. PCG64 — быстрый, но не криптостойкий. Для секретов — модуль secrets.

См. также

Примечание

Лицензия и источники

Техническое описание адаптировано из официальной документации NumPy (https://numpy.org/doc/stable/), доступной под BSD-3-Clause License. Перевод на русский, примеры и пояснения — © AlashEd Wiki.