> pick | shuffle | pair <

// 목록을 붙여넣고, 당첨자를 뽑고, 순서를 섞거나, 사람들을 그룹으로 나누세요 - 모두 브라우저 안에서

[PICK_N]

N개 무작위 항목 뽑기

목록에서 원하는 수의 당첨자를 뽑습니다. 고유한 당첨자를 위해 비복원으로, 또는 가중치 방식의 추첨을 위해 복원으로 뽑을 수 있습니다.

[SHUFFLE]

편향 없는 피셔-예이츠 셔플

고전적인 크누스 셔플로 전체 목록을 재정렬합니다. 모든 순열이 동일하게 일어날 수 있습니다 - 안일한 정렬 꼼수는 없습니다.

[PAIRS]

그룹으로 나누기

명단을 둘, 셋 또는 임의의 그룹 크기로 나눕니다. 짝 활동, 코드 리뷰 순환, 시크릿 산타 짝짓기에 적합합니다.

[SEEDED]

시드 기반 재현성

시드 문자열을 제공하면 결정론적인 추첨이 됩니다. 같은 시드와 같은 목록이면 항상 같은 결과가 나와 공유하고 검증할 수 있습니다.

// 랜덤 추첨 도구 소개

작동 방식:

이 도구는 피셔-예이츠(일명 크누스) 셔플을 사용합니다. 배열을 마지막 인덱스부터 처음까지 훑으며, 각 단계에서 현재 요소를 균등하게 무작위로 선택한 더 앞쪽 인덱스의 요소와 교환합니다. 이렇게 하면 O(n) 시간에 편향 없는 균등한 순열이 생성됩니다. 안일한 방법인 array.sort(() => Math.random() - 0.5)는 편향됩니다. JavaScript 정렬 비교자는 추이적이어야 하는데, 무작위 비교자는 이를 위반하여 분포를 왜곡하기 때문입니다. 시드 없는 추첨의 경우 window.crypto.getRandomValues에서 엔트로피를 가져옵니다. 시드 문자열을 제공하면 cyrb53으로 해시하여 53비트 정수로 만든 다음 mulberry32 PRNG를 구동하므로, 동일한 시드는 동일하고 재현 가능한 결과를 생성합니다.

알고리즘:

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 - Tommy Ettinger가 만든 작고 빠르며 분포가 좋은 시드 기반 PRNG
  • >cyrb53 - bryc가 만든 53비트 문자열 해시로, 시드 문자열을 숫자 PRNG 상태로 변환하는 데 사용

일반적인 사용 사례:

  • >경품 행사, 추첨, 응모자 중 무작위 당첨자 선정
  • >교실에서 이름 뽑기 및 학생 즉석 지명 선택
  • >스포츠 팀 짝짓기, 짝 운동, 토너먼트 대진표
  • >엔지니어링 팀 전반의 코드 리뷰 검토자 배정
  • >A/B 테스트 표본 선택 및 무작위화된 설문 코호트
  • >시크릿 산타 짝, 선물 교환, 팀 점심 동료
  • >QA를 위한 무작위 테스트 입력 및 퍼징 시드
  • >그룹 기반 의사 결정 및 동점 해결
  • >모의 배심원 선정 및 무작위화된 면접 패널
  • >지역 행사 및 상품을 위한 복권식 추첨

관련 도구:

  • >난수 생성기 — 텍스트 기반 동반 도구: 동일한 시드 재현성 기본 요소로 범위 내 숫자를 뽑습니다
  • >UUID 생성기 — 목록에서 당첨자를 뽑는 대신 고유한 불투명 ID가 필요할 때 쓰는 무작위 v4 식별자
  • >비밀번호 생성기 — 토큰과 자격 증명을 위해 문자 풀을 제어할 수 있는 암호학적으로 무작위인 문자열
  • >randompickerwheel.app — 이 텍스트 추첨을 보완하는 돌림판 UI로, 라이브 관객 추첨, 교실 지명, 화면 공개에 적합합니다

// 추첨 예시

경품 행사 — 50건의 응모에서 당첨자 3명 뽑기

> 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를 사용했습니다. 각 구현은 서로 다른 비교 쌍의 부분집합을 방문하고 캐시된 비교를 서로 다른 방식으로 재사용합니다. Mike Bostock이 실증적으로 보여주었듯이(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);

>> 자주 묻는 질문

Q: 무작위성은 어떻게 생성되나요?

A: 시드가 제공되지 않으면 Web Crypto API를 기반으로 하는 브라우저의 암호학적으로 안전한 난수 생성기인 window.crypto.getRandomValues를 사용합니다. 이는 암호 키와 토큰에 사용되는 것과 동일한 엔트로피 소스이므로 추첨과 셔플에는 충분히 강력합니다. 시드를 제공하면 cyrb53 문자열 해시로 53비트 상태를 결정론적으로 도출하여 mulberry32 PRNG에 입력하므로 추첨이 재현 가능합니다.

Q: 시드를 사용해 같은 결과를 두 번 얻을 수 있나요?

A: 네. SEED 필드에 아무 문자열이나 입력하세요 - 날짜, 콘테스트 이름, 해시 등 무엇이든 좋습니다. 같은 시드를 같은 입력 목록 및 같은 모드와 결합하면 항상 정확히 같은 출력이 생성됩니다. 이는 시드를 미리 공개하고 참가자가 추첨을 검증하도록 하려는 투명한 경품 행사나, 여러 머신에서 재현 가능해야 하는 엔지니어링 순환에 매우 유용합니다.

Q: Pick N과 Shuffle의 차이는 무엇인가요?

A: Pick N은 목록에서 크기 N의 부분집합을 선택하며, 선택적으로 복원 추출(중복 허용)도 가능합니다. Shuffle은 전체 목록을 재정렬하여 반환합니다 - 항목이 제거되거나 반복되지 않고 단지 재배치됩니다. 많은 응모 중 소수의 당첨자만 원할 때는 Pick N을, 모든 항목이 여전히 참여하되 새로운 무작위 순서가 필요할 때(발표 대기열이나 재생 목록 등)는 Shuffle을 사용하세요.

Q: array.sort(() => Math.random() - 0.5)는 정말 무작위인가요?

A: 아니요, 편향되어 있습니다. ECMAScript 사양은 정렬 비교자가 결정론적이고 추이적일 것을 요구합니다(a < b이고 b < c이면 a < c). 무작위 비교자는 이 계약을 위반하며, 서로 다른 JavaScript 엔진은 서로 다른 기반 정렬 알고리즘(TimSort, mergesort, quicksort)을 실행하고 각기 다른 방식으로 무작위 비교자와 상호작용합니다. 그 결과 일부 순열이 다른 것보다 훨씬 자주 나타나는 균등하지 않은 분포가 됩니다. 피셔-예이츠가 올바른 알고리즘입니다.

Q: 홀수 개의 목록을 짝지을 수 있나요?

A: 네. Pair Up 모드는 피셔-예이츠로 목록을 섞은 다음 선택한 그룹 크기로 나눕니다. 총합이 균등하게 나뉘지 않으면 마지막 그룹은 더 작아지고 출력에서 나머지 그룹으로 명확히 표시됩니다. 홀수 목록의 경우 단독 항목을 허용하거나, 그룹 크기를 늘리거나, 추첨 전에 "bye" 같은 자리표시자 이름을 추가할 수 있습니다.

Q: 제 목록이 어딘가에 업로드되나요?

A: 아니요. 이 도구는 100% 클라이언트 측에서 작동합니다 - 목록, 시드, 출력이 브라우저를 떠나는 일은 결코 없습니다. 서버 호출도, 로깅도, 입력에 대한 분석도 없습니다. 페이지가 로드된 후 인터넷 연결을 끊어도 모든 버튼이 계속 작동합니다. 페이지를 새로고침하면 데이터는 영구히 사라집니다.

Q: 이 도구의 시각적 돌림판 버전이 있나요?

A: 애니메이션, 음향 효과, 큰 시각적 공개가 있는 인터랙티브 이름 돌림판 경험은 randompickerwheel.app를 참고하세요. 라이브 관객 추첨과 화면 공유에 중점을 둔 보완 제품입니다. 이 페이지는 대량 목록, 재현 가능한 시드 추첨, CSV 내보내기, 짝짓기에 중점을 둔 텍스트 우선 추첨 도구입니다 - 프레젠테이션보다는 빠른 결정론적 답이 필요할 때 유용합니다.

// OTHER LANGUAGES