> pick | shuffle | pair <

// Вставьте список, выберите победителей, перемешайте порядок или разбейте людей на группы - всё в вашем браузере

[PICK_N]

Выбрать N случайных элементов

Выберите любое количество победителей из вашего списка. Без возвращения для уникальных победителей или с возвращением для розыгрышей во взвешенном стиле.

[SHUFFLE]

Несмещённое перемешивание Фишера-Йейтса

Переупорядочивает весь список с помощью классического перемешивания Кнута. Каждая перестановка равновероятна - никаких наивных трюков с сортировкой.

[PAIRS]

Разбить на группы

Разделите список на пары, тройки или группы любого размера. Идеально для работы в парах, ротаций ревью кода или жеребьёвок «Тайного Санты».

[SEEDED]

Воспроизводимость с семенем

Укажите строку семени, чтобы получить детерминированный розыгрыш. Одно и то же семя с одним и тем же списком всегда даёт один и тот же результат - им можно делиться и его можно проверять.

// О ИНСТРУМЕНТЕ СЛУЧАЙНОГО ВЫБОРА

Как это работает:

Этот инструмент использует перемешивание Фишера-Йейтса (также известное как перемешивание Кнута): пройдите по массиву от последнего индекса к первому и на каждом шаге поменяйте текущий элемент с элементом по равномерно случайному более раннему индексу. Это даёт несмещённую равномерную перестановку за время O(n). Наивный трюк array.sort(() => Math.random() - 0.5) смещён, потому что компараторы сортировки в JavaScript должны быть транзитивными - случайные компараторы нарушают это и искажают распределение. Для розыгрышей без семени мы берём энтропию из window.crypto.getRandomValues. Когда вы указываете строку семени, мы хешируем её с помощью cyrb53 в 53-битное целое число, а затем запускаем PRNG mulberry32, чтобы одинаковые семена давали одинаковые, воспроизводимые результаты.

Алгоритм:

for i = n-1 down to 1: j = randInt(0, i); swap(a[i], a[j]) - unbiased Fisher-Yates

Стандарты и ссылки:

  • >Web Crypto API (W3C) - crypto.getRandomValues для криптографически стойкой случайности
  • >Перемешивание Фишера-Йейтса (1938), популяризированное Дональдом Кнутом в The Art of Computer Programming, том 2
  • >mulberry32 - маленький, быстрый, хорошо распределённый PRNG с семенем от Tommy Ettinger
  • >cyrb53 - 53-битный хеш строки от bryc, используемый для превращения строки семени в числовое состояние PRNG

Распространённые сценарии использования:

  • >Раздачи призов, лотереи и определение случайного победителя среди участников
  • >Жеребьёвка имён в классе и выбор учеников для неожиданных вопросов
  • >Составление спортивных пар, парные тренировки и турнирные сетки
  • >Назначение ревьюеров кода в инженерной команде
  • >Выбор выборки для A/B-тестов и рандомизированные опросные когорты
  • >Пары «Тайного Санты», обмен подарками и напарники для обеда в команде
  • >Случайные тестовые данные и семена фаззинга для QA
  • >Групповое принятие решений и разрешение ничьих
  • >Подбор имитационного жюри и рандомизированные интервью-панели
  • >Лотерейные розыгрыши для общественных мероприятий и призов

Связанные инструменты:

  • >Генератор случайных чисел — текстовый аналог: выбирайте числа в диапазоне с теми же примитивами воспроизводимости с семенем
  • >Генератор UUID — случайные идентификаторы v4, когда нужен уникальный непрозрачный ID, а не победитель из списка
  • >Генератор паролей — криптографически случайные строки с управлением набором символов для токенов и учётных данных
  • >randompickerwheel.app — интерфейс колеса фортуны, дополняющий этот текстовый выбор, идеально подходит для розыгрышей перед живой аудиторией, вызовов в классе и показа на экране

// ПРИМЕРЫ ВЫБОРА

Розыгрыш — выбрать 3 победителей из 50 участников

> input:

alice@x.com
bob@x.com
carlos@x.com
…(всего 50 строк)

> config:

Режим=Pick N, Количество=3, Разрешить повторы=off, Семя=(пусто)

> output:

1. carlos@x.com
2. priya@x.com
3. wei@x.com

Перемешать список из 10 имён для порядка выступлений

> input:

Alice
Bob
Charlie
Dana
Evelyn
Farouk
Gabriela
Hiroshi
Isabella
Jamal

> config:

Режим=Shuffle, Семя=(пусто)

> output:

1. Hiroshi
2. Alice
3. Farouk
4. Jamal
5. Dana
6. Evelyn
7. Bob
8. Isabella
9. Gabriela
10. Charlie

Разбить класс из 12 учеников на группы по 2

> input:

S01
S02
S03
S04
S05
S06
S07
S08
S09
S10
S11
S12

> config:

Режим=Pair Up, Размер группы=2, Семя=class-2026-05-02

> output:

Группа 1: S07, S02
Группа 2: S11, S04
Группа 3: S09, S12
Группа 4: S01, S06
Группа 5: S03, S10
Группа 6: S08, S05

Воспроизводимый розыгрыш с семенем — запустите дважды с одним и тем же семенем, получите тех же победителей

> input:

alpha
bravo
charlie
delta
echo
foxtrot
golf
hotel

> config:

Режим=Pick N, Количество=2, Семя=launch-day, Разрешить повторы=off

> output:

Запуск 1: 1. echo
          2. bravo
Запуск 2: 1. echo
          2. bravo  (идентично — с семенем)

// ПОЧЕМУ НАИВНЫЙ .SORT() СМЕЩЁН

Распространённое перемешивание в одну строку — arr.sort(() => Math.random() - 0.5). Выглядит элегантно, помещается в одну строку и кажется случайным — но получаемое распределение перестановок далеко от равномерного. Спецификация ECMAScript требует, чтобы функции-компараторы были детерминированными и транзитивными: если a < b и b < c, то a < c. Компаратор «орёл или решка» нарушает этот контракт, и реализация сортировки движка посещает пары сравнения по неравномерному шаблону.

V8 (Chrome, Node) использует Timsort; более старые движки использовали quicksort или mergesort. Каждая реализация посещает разное подмножество пар сравнения и по-разному переиспользует кешированные сравнения. Как эмпирически показал Майк Босток (bost.ocks.org/mike/shuffle/compare.html), наивная сортировка даёт сильно смещённые результаты, при которых некоторые перестановки встречаются в несколько раз чаще других — что недопустимо для розыгрыша, A/B-корзины или любого жребия, который должен быть честным.

Правильный алгоритм — Фишер-Йейтс (перемешивание Кнута): пройдите по массиву от последнего индекса к первому и на каждом шаге поменяйте текущий элемент с элементом, выбранным равномерно случайно среди индексов 0..i. Он работает за O(n), использует O(1) дополнительной памяти и доказуемо даёт каждую перестановку с равной вероятностью.

// BAD: NOT uniformly random
const shuffled = arr.sort(() => Math.random() - 0.5);

// GOOD: Fisher-Yates (Knuth) shuffle
function shuffle(arr) {
  const a = arr.slice();
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

// РЕАЛИЗУЙТЕ САМИ

Хотите создать собственный инструмент выбора или проверить наш? Это в точности те примитивы, которые использует эта страница — маленькие, без зависимостей и идентичные тому, что выполняется в вашем браузере при нажатии PICK. Скопируйте их прямо в скрипт Node или другое веб-приложение.

// Fisher-Yates shuffle (uniform, O(n))

function shuffle(arr, rng = Math.random) {
  const a = arr.slice();
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(rng() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

// Pick N without replacement (shuffle then slice)

function pickN(arr, n, rng = Math.random) {
  if (n > arr.length) {
    throw new Error('n exceeds list size');
  }
  return shuffle(arr, rng).slice(0, n);
}

// mulberry32 seeded PRNG + cyrb53 string hash

// cyrb53: turn a seed string into a 53-bit integer
function cyrb53(str, seed = 0) {
  let h1 = 0xdeadbeef ^ seed;
  let h2 = 0x41c6ce57 ^ seed;
  for (let i = 0, ch; i < str.length; i++) {
    ch = str.charCodeAt(i);
    h1 = Math.imul(h1 ^ ch, 2654435761);
    h2 = Math.imul(h2 ^ ch, 1597334677);
  }
  h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
       Math.imul(h2 ^ (h2 >>> 13), 3266489909);
  h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
       Math.imul(h1 ^ (h1 >>> 13), 3266489909);
  return 4294967296 * (2097151 & h2) + (h1 >>> 0);
}

// mulberry32: tiny seeded PRNG -> [0, 1)
function mulberry32(a) {
  return function () {
    a |= 0; a = (a + 0x6D2B79F5) | 0;
    let t = a;
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

// Build a seeded picker:
const h = cyrb53('my-seed', 1);
const state = (h ^ Math.floor(h / 4294967296)) >>> 0;
const rng = mulberry32(state);
const winners = pickN(['alice', 'bob', 'carlos'], 2, rng);

>> часто задаваемые вопросы

В: Как генерируется случайность?

О: Когда семя не указано, мы используем window.crypto.getRandomValues — криптографически безопасный генератор случайных чисел браузера на основе Web Crypto API. Это тот же источник энтропии, что используется для криптографических ключей и токенов, поэтому его более чем достаточно для лотерей и перемешиваний. Когда вы указываете семя, мы детерминированно выводим 53-битное состояние через хеш строки cyrb53 и подаём его в PRNG mulberry32, чтобы розыгрыш был воспроизводимым.

В: Можно ли получить один и тот же результат дважды, используя семя?

О: Да. Введите любую строку в поле СЕМЯ - дату, название конкурса, хеш, что угодно. Одно и то же семя в сочетании с одним и тем же списком ввода и одним и тем же режимом всегда даст в точности один и тот же результат. Это бесценно для прозрачных розыгрышей, где вы хотите опубликовать семя заранее и дать участникам проверить розыгрыш, или для инженерных ротаций, которые должны быть воспроизводимы на разных машинах.

В: В чём разница между Pick N и Shuffle?

О: Pick N выбирает подмножество размера N из вашего списка, при необходимости с возвращением (повторы разрешены). Shuffle возвращает весь список в переупорядоченном виде - ни один элемент не отбрасывается и не повторяется, только переставляется. Используйте Pick N, когда вам нужно лишь несколько победителей из многих участников; используйте Shuffle, когда каждый элемент по-прежнему должен участвовать, но в новом случайном порядке, например очередь выступлений или плейлист.

В: Действительно ли array.sort(() => Math.random() - 0.5) случаен?

О: Нет, он смещён. Спецификация ECMAScript требует, чтобы компараторы сортировки были детерминированными и транзитивными (если a < b и b < c, то a < c). Случайный компаратор нарушает этот контракт, а разные движки JavaScript выполняют разные базовые алгоритмы сортировки (TimSort, mergesort, quicksort), которые по-разному взаимодействуют со случайным компаратором. Результат — неравномерное распределение, при котором некоторые перестановки встречаются гораздо чаще других. Фишер-Йейтс — это правильный алгоритм.

В: Можно ли разбить на пары список с нечётным количеством элементов?

О: Да. Режим Pair Up перемешивает ваш список с помощью Фишера-Йейтса, а затем разбивает его по выбранному размеру группы. Если общее число не делится нацело, последняя группа будет меньше и чётко помечена в выводе как остаточная группа. Для нечётного списка вы можете либо принять одиночную запись, либо увеличить размер группы, либо перед розыгрышем добавить имя-заполнитель, например «bye».

В: Загружается ли мой список куда-либо?

О: Нет. Этот инструмент работает на 100% на стороне клиента - ваш список, ваше семя и вывод никогда не покидают ваш браузер. Нет ни вызовов сервера, ни логирования, ни аналитики по вводу. Вы можете отключиться от интернета после загрузки страницы, и каждая кнопка по-прежнему будет работать. Обновите страницу — и данные исчезнут навсегда.

В: Есть ли визуальная версия этого инструмента в виде колеса фортуны?

О: Для интерактивного колеса имён с анимацией, звуковыми эффектами и эффектным визуальным показом см. randompickerwheel.app. Это дополнительный продукт, ориентированный на розыгрыши перед живой аудиторией и демонстрацию экрана. Эта страница — текстовый инструмент выбора, ориентированный на большие списки, воспроизводимые розыгрыши с семенем, экспорт CSV и разбиение на пары - полезен, когда нужен быстрый детерминированный ответ, а не презентация.

// OTHER LANGUAGES