Инструмент случайного выбора
Инструмент справедливого случайного выбора для розыгрышей, жеребьёвок и выборов
Что такое инструмент случайного выбора?
Случайный выборщик выбирает один элемент из списка без ручного решения человека. Он полезен для вызова учеников, порядка выступлений на встрече, небольших розыгрышей, распределения задач, выбора обеда, командных разминок и ситуаций, где видимый нейтральный выбор уменьшает предвзятость. Эта страница работает со списком по строкам, может показывать анимацию выбора, хранить локальную историю и исключать повторы, удаляя выбранные элементы из оставшегося пула. Случайность помогает статистической справедливости, но не проверяет личности, не удаляет автоматически дубликаты имен и не дает аудиторских доказательств. Для официальных розыгрышей, проверки права участия и выдачи призов нужен процесс с записями и правилами.
Как использовать
Этапы выполнения
- Введите варианты в текстовое поле, по одному на строку
- Задайте количество элементов для выбора
- Выберите, разрешать ли дубликаты
- Нажмите кнопку «Start Draw»
- Результаты отображаются справа
Советы для честного розыгрыша
- Размещайте по одному кандидату в строке и удаляйте случайные пустые записи и дубликаты, если только повторы не разрешены намеренно.
- Для публичных розыгрышей заранее определите seed, правила обработки дубликатов и перерозыгрыша, чтобы результат было проще объяснить.
Применение
Технический принцип
Инструмент случайного выбора основан на Web Crypto API (W3C, WHATWG), а именно на crypto.getRandomValues(typedArray) — единственном криптографически стойком источнике случайных чисел, доступном в браузерах. На низком уровне браузер обращается к CSPRNG операционной системы: CryptGenRandom на Windows (AES-256 в режиме CTR-DRBG начиная с Windows 10), getrandom(2) на Linux (читает из того же пула на основе ChaCha20, который питает /dev/urandom ядра), SecRandomCopyBytes на macOS и эквиваленты на iOS, Android и BSD. Выходные данные пригодны для генерации ключей, одноразовых номеров, солей, IV и любых мест, где предсказуемость создала бы проблему безопасности. Различие с «криптографическим» качеством важно. Math.random() в V8 использует xorshift128+ (быстрый алгоритм с низкой энтропией) и возвращает 64-битное число с плавающей точкой в [0, 1); злоумышленник, наблюдающий несколько выходов, может теоретически восстановить 128-битное состояние и предсказать каждое будущее значение. То же самое верно для SpiderMonkey и JavaScriptCore. Это делает Math.random() непригодным для любого использования, где результат должен быть неугадываемым — справедливый выбор победителя, токен, одноразовый пароль, сделка в карточной игре, выборка для аудита. crypto.getRandomValues(typedArray) — единственный выбор для таких задач. Отображение равномерных 32-битных целых чисел в равномерный индекс в [0, N) без смещения по модулю — второй инженерный момент, отличающий корректное решение от ошибочного. Наивный подход idx = randomUint32 % N неверен: если N не делит 2³², первые 2³² mod N значений несколько более вероятны. Корректный алгоритм — отбраковка: вычислить limit = 2³² - (2³² mod N); сгенерировать 32-битное значение r; если r >= limit, перегенерировать; иначе вернуть r % N. Смещение падает с O(N / 2³²) до нуля. Для N = 2 или N — степени двойки взятие по модулю точно, но отбраковка — безопасный вариант по умолчанию и в среднем требует не более одной перегенерации (и всегда завершается). Выборщик хранит результаты в localStorage с версионированным ключом, поэтому сессия переживает перезагрузку страницы, а пользователь может просматривать историю розыгрышей. История не зашифрована и не подписана — она локальная, нечувствительная и легко очищается. Для режима без повторов алгоритм выполняет выбор без возвращения с помощью частичного тасования Фишера-Йетса: на шаге k из N элемент k меняется местами с равномерно выбранным элементом из [k, N), после чего элемент k фиксируется как выбранный. Это O(N) по времени, O(1) дополнительной памяти, и каждая перестановка имеет равную вероятность, что является правильной комбинаторной гарантией. (Наивный «выбрать N раз, отбрасывая повторы» тоже корректен, но O(N²) и может застрять на длинных сессиях.) Для взвешенного выбора (каждый вариант имеет свою вероятность) стандартный алгоритм — обратное CDF-выборка с массивом префиксных сумм: построить префиксную сумму весов, сгенерировать равномерное вещественное число в [0, total) и бинарным поиском найти наименьший i, для которого prefix[i] >= u. Это O(log N) на выборку, а префиксная сумма строится один раз при изменении. Метод алиасов (Уокера-Воуза) даёт O(1) на выборку ценой O(N) настройки, что целесообразно только при большом N и фиксированном распределении.
- Источник случайности: crypto.getRandomValues(typedArray) — единственный криптографически стойкий ГСЧ в браузерах, основанный на CSPRNG операционной системы (AES-256 CTR-DRBG на Windows 10+, ChaCha20 на Linux/macOS/iOS/Android).
- Math.random() — это xorshift128+ в V8 (и варианты в других движках): быстрый, но предсказуемый. Никогда не используйте его для справедливого выбора, токенов, OTP или выборок для аудита — только для визуального шума, анимаций или несвязанных с безопасностью задач.
- Неискажённое отображение индекса: отбраковка, а не взятие по модулю. Вычислить limit = 2³² - (2³² mod N); если случайный uint32 >= limit, перегенерировать; иначе вернуть r % N. Избегает классического «смещения по модулю», когда индексы с коротким диапазоном выпадают чаще.
- Выбор без повторов использует частичное тасование Фишера-Йетса за O(N) времени, O(1) памяти: на шаге k меняет местами элемент k с равномерным элементом из [k, N), затем фиксирует k. Каждая перестановка равновероятна.
- Взвешенный выбор использует обратное CDF-выборку: строит префиксную сумму весов один раз, генерирует равномерное вещественное число в [0, total), бинарным поиском находит наименьший prefix[i] >= u. O(log N) на выборку.
- История розыгрышей сохраняется в localStorage с версионированным ключом, поэтому сессия переживает перезагрузку страницы. История хранится только локально и не является конфиденциальной — очистите её из интерфейса по завершении.
- Фишер-Йетс vs «выбрать N раз с отбраковкой»: оба корректны по распределению, но Фишер-Йетс — O(N) суммарно и O(1) по памяти, тогда как наивная отбраковка — O(N²) в худшем случае (много повторов в конце длинного списка).
- Диапазон и типы выходных данных: getRandomValues принимает Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, BigUint64Array — страница всегда использует Uint32Array для этапа отображения индекса.
Примеры
Выбор без повторов из списка кандидатов
Список (по одному в строке):
Alice
Bob
Charlie
David
Eve
Frank
Разрешить дубликаты: ВЫКЛ Выборок: 3
Результат:
#1: Charlie
#2: Alice
#3: Frank
(Bob, David, Eve остаются в пуле)
Страница вызывает crypto.getRandomValues() для Uint32Array, чтобы
сгенерировать равномерное целое число в [0, n) для каждой выборки, где n
— количество оставшихся кандидатов. Каждое имя имеет одинаковую
вероятность 1/n при каждой выборке, и выбранный индекс фиксируется
до начала анимации.Выбор с повторами и историей с временными метками
Список (по одному в строке):
Head
Tails
Разрешить дубликаты: ВКЛ Выборок: 5
Результат:
#1 (14:23:01): Head
#2 (14:23:01): Tails
#3 (14:23:01): Head
#4 (14:23:01): Head
#5 (14:23:01): Tails
В режиме повторов одна и та же запись может быть выбрана подряд, и пул
не сокращается. Временная метка выборки добавляется к каждой записи,
чтобы один и тот же список кандидатов можно было использовать в разных
раундах, при этом история точно показывает, когда произошла каждая
выборка. Очищайте записи отдельно от списка кандидатов — они хранятся
под разными ключами localStorage.JavaScript-фрагмент для базовой выборки
// Выбрать один индекс из [0, n) с помощью Web Crypto (CSPRNG)
function pickIndex(n) {
// crypto.getRandomValues даёт равномерное 32-битное значение; modulo n
// безопасно здесь, потому что n не более нескольких тысяч для выборки
// из списка, и смещение по модулю пренебрежимо мало (2^32 / n велико).
const buf = new Uint32Array(1);
crypto.getRandomValues(buf);
return buf[0] % n;
}
// pickIndex(6) -> например, 4 (Charlie в примере выше)
// pickIndex(2) -> 0 или 1 (Head или Tails)
//
// Примечание: для огромных пулов, где n приближается к 2^32, используйте
// rejection sampling, чтобы устранить смещение по модулю, например:
// const limit = Math.floor(0xFFFFFFFF / n) * n;
// let r; do { crypto.getRandomValues(buf); r = buf[0]; } while (r >= limit);Проверка справедливости на 1 000 выборках
Список: ['A', 'B', 'C', 'D'] (4 записи, ожидаемая доля 25.0%)
Выборок: 1 000 Разрешить дубликаты: ВКЛ
Наблюдается:
A: 247 (24.7%)
B: 256 (25.6%)
C: 248 (24.8%)
D: 249 (24.9%)
Прогон в 1 000 выборок с crypto.getRandomValues должен попадать
примерно в +/- 3 процентных пункта от ожидаемой доли по каждой записи;
большие отклонения (например, одна запись на 35%) обычно означают, что
реализация вызывает Math.random() или неправильно применяет операцию
по модулю. Для регламентированной лотереи сохраняйте исходный список,
источник случайного зерна (энтропия браузера/ОС), временную метку
выборки и подпись свидетеля — localStorage не является долговременной
системой учёта.Часто задаваемые вопросы
Действительно ли выбор случайный?
Да. Выбор основан на crypto.getRandomValues из Web Crypto API — это криптографически стойкий генератор. Если вы не задали веса, у каждого варианта одинаковая вероятность.
Может ли один и тот же вариант выпасть два раза подряд?
Да — так и работает независимый случайный выбор. При 5 вариантах вероятность двух одинаковых результатов подряд составляет 1/5 = 20%. Если повторы вам не нужны, включите режим «удалять после выбора» — тогда выбор идёт без возврата, пока список не опустеет.
Как работает режим «выбрать N элементов»?
По умолчанию выборка идёт без возврата — каждый элемент может попасть в результат не более одного раза за один тираж из N. Включите «с возвратом», если дубликаты допустимы (например, выбор N задач для одного человека). В режиме без возврата N не может превышать размер списка.
Можно ли задать веса, чтобы сместить вероятности?
В некоторых сборках поддерживаются веса для каждого варианта (например, у варианта A вес 3, у варианта B — 1, тогда A выпадает в 3 раза чаще). Страница нормализует веса в вероятности. Без весов у всех вариантов шансы равны.
Почему это честнее, чем выбор «на глазок»?
Люди плохо справляются со случайностью — мы избегаем недавно выпавших вариантов, чаще выбираем заметные пункты и подсознательно склоняемся к знакомым именам. Компьютерный выбор устраняет эти искажения, и решения вроде розыгрышей выглядят для участников более честными.
Сохраняется ли мой список?
В некоторых сборках список сохраняется в localStorage и остаётся доступным в том же браузере. Если закрыть вкладку или сменить браузер, данные пропадут — экспортируйте их заранее. Ничего никуда не загружается.
Можно ли использовать инструмент для официальных лотерей?
Нет. Юридически значимые розыгрыши, лотереи и азартные тиражи требуют проверяемых процедур, часто на физическом или сертифицированном ГСЧ. Универсальный веб-инструмент не имеет документации и аудита — пользуйтесь им только для офисных розыгрышей и неформальных жеребьёвок.