Для выбора победителей используется crypto.getRandomValues() —
криптографически стойкий генератор случайных чисел браузера (CSPRNG).
Он опирается на системный источник энтропии операционной системы (шум железа,
прерывания и т.д.), а не на детерминированную математическую формулу.
❌ Math.random()
Псевдослучайный (PRNG). Предсказуем при знании начального состояния.
Не подходит для честного розыгрыша.
✅ crypto.getRandomValues()
Криптографически стойкий. Непредсказуем. Стандарт W3C. Работает во всех современных браузерах.
⚖️Защита от смещения
Простое взятие остатка rand % N создаёт modulo bias —
одни участники имеют чуть большую вероятность. При 10 участниках и 32-битном числе
погрешность до +0.0023%. Мы устраняем это методом Rejection Sampling:
1
Вычисляем limit = floor(2³²/ N) × N — максимум без остатка
2
Генерируем случайное 32-битное число
3
Если число ≥ limit — отбрасываем и повторяем (в среднем <2 попытки)
4
Берём r % N — теперь каждый индекс равновероятен
🎡Честность колеса
Угол остановки рассчитывается в два этапа:
1
Целевой участник выбирается через crypto.getRandomValues() — каждый с вероятностью 1/N
2
Угол выбирается случайно внутри сегмента — колесо не останавливается ровно по центру, а в произвольной точке
3
Анимация вращения — только визуализация. Победитель определён до начала анимации
🎲Честность рандомайзера
Рандомайзер работает идентично колесу, но без анимации вращения.
При каждом нажатии «Выбрать!»:
1
Из текущего списка оставшихся участников выбирается индекс через rejection sampling
2
Мельтешащие имена — лишь анимация, победитель определён заранее
3
При включённом «Убирать победителя» — выбранный удаляется из пула, исключая повторы
📊Живой тест равномерности распределения
Запусти симуляцию N розыгрышей на текущих участниках и убедись, что каждый выбирается примерно одинаково часто.
Загрузи участников во вкладке «Колесо», затем нажми кнопку.