> pick | shuffle | pair <

// Plak een lijst, kies winnaars, schud de volgorde of verdeel mensen in groepen - allemaal in je browser

[PICK_N]

Kies N willekeurige items

Trek een willekeurig aantal winnaars uit je lijst. Zonder teruglegging voor unieke winnaars, of met teruglegging voor trekkingen in gewogen stijl.

[SHUFFLE]

Onbevooroordeelde Fisher-Yates-shuffle

Herordent de hele lijst met de klassieke Knuth-shuffle. Elke permutatie is even waarschijnlijk - geen naïeve sorteertrucs hier.

[PAIRS]

In groepen verdelen

Verdeel een lijst in paren, trio's of een willekeurige groepsgrootte. Perfect voor werken in tweetallen, code-reviewrotaties of Secret Santa-koppelingen.

[SEEDED]

Reproduceerbaarheid met seed

Geef een seed-string op voor een deterministische trekking. Dezelfde seed plus dezelfde lijst levert altijd hetzelfde resultaat op - deelbaar en verifieerbaar.

// OVER DE WILLEKEURIGE KIEZER

Hoe het werkt:

Deze tool gebruikt de Fisher-Yates- (ook wel Knuth-) shuffle: loop de array van de laatste index naar de eerste en verwissel bij elke stap het huidige element met één op een uniform willekeurige eerdere index. Dit levert in O(n)-tijd een onbevooroordeelde, uniforme permutatie op. De naïeve truc array.sort(() => Math.random() - 0.5) is bevooroordeeld omdat JavaScript-sorteervergelijkers transitief moeten zijn - willekeurige vergelijkers schenden dat en vertekenen de verdeling. Voor trekkingen zonder seed halen we entropie uit window.crypto.getRandomValues. Wanneer je een seed-string opgeeft, hashen we die met cyrb53 naar een 53-bits geheel getal en voeden we daarmee een mulberry32-PRNG, zodat identieke seeds identieke, reproduceerbare resultaten opleveren.

Algoritme:

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

Standaarden & referenties:

  • >Web Crypto API (W3C) - crypto.getRandomValues voor cryptografisch sterke willekeur
  • >Fisher-Yates-shuffle (1938), populair gemaakt door Donald Knuth in The Art of Computer Programming Vol. 2
  • >mulberry32 - kleine, snelle, goed verdeelde seed-PRNG van Tommy Ettinger
  • >cyrb53 - 53-bits string-hash van bryc, gebruikt om een seed-string om te zetten in een numerieke PRNG-toestand

Veelvoorkomende toepassingen:

  • >Weggeefacties, loterijen en het aanwijzen van een willekeurige winnaar uit inzendingen
  • >Naamtrekkingen in de klas en het selecteren van leerlingen voor onverwachte vragen
  • >Sportteamkoppeling, trainingen in tweetallen en toernooischema's
  • >Toewijzing van code-reviewers binnen een engineeringteam
  • >Steekproefselectie voor A/B-tests en gerandomiseerde enquêtecohorten
  • >Secret Santa-paren, cadeau-uitwisselingen en teamlunchmaatjes
  • >Willekeurige testinvoer en fuzzing-seeds voor QA
  • >Groepsgewijze besluitvorming en beslissingen bij gelijke stand
  • >Selectie van een schijnjury en gerandomiseerde sollicitatiepanels
  • >Loterijachtige trekkingen voor gemeenschapsevenementen en prijzen

Gerelateerde tools:

  • >Willekeurige getallengenerator — het op tekst gebaseerde tegenhanger: kies getallen in een bereik met dezelfde seed-reproduceerbaarheidsprimitieven
  • >UUID-generator — willekeurige v4-identifiers wanneer je een unieke ondoorzichtige ID nodig hebt in plaats van een winnaar uit een lijst
  • >Wachtwoordgenerator — cryptografisch willekeurige strings met controle over de tekenpool voor tokens en inloggegevens
  • >randompickerwheel.app — rad-van-namen-interface als aanvulling op deze tekstkiezer, perfect voor live trekkingen voor publiek, vragen in de klas en onthullingen op het scherm

// VOORBEELDTREKKINGEN

Weggeefactie — kies 3 winnaars uit 50 inzendingen

> input:

alice@x.com
bob@x.com
carlos@x.com
…(50 regels in totaal)

> config:

Modus=Pick N, Aantal=3, Duplicaten toestaan=off, Seed=(leeg)

> output:

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

Schud een lijst van 10 namen voor een presentatievolgorde

> input:

Alice
Bob
Charlie
Dana
Evelyn
Farouk
Gabriela
Hiroshi
Isabella
Jamal

> config:

Modus=Shuffle, Seed=(leeg)

> output:

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

Verdeel een klas van 12 leerlingen in groepen van 2

> input:

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

> config:

Modus=Pair Up, Groepsgrootte=2, Seed=class-2026-05-02

> output:

Groep 1: S07, S02
Groep 2: S11, S04
Groep 3: S09, S12
Groep 4: S01, S06
Groep 5: S03, S10
Groep 6: S08, S05

Reproduceerbare trekking met seed — voer twee keer uit met dezelfde seed, krijg dezelfde winnaars

> input:

alpha
bravo
charlie
delta
echo
foxtrot
golf
hotel

> config:

Modus=Pick N, Aantal=2, Seed=launch-day, Duplicaten toestaan=off

> output:

Run 1: 1. echo
       2. bravo
Run 2: 1. echo
       2. bravo  (identiek — met seed)

// WAAROM NAÏEF .SORT() BEVOOROORDEELD IS

Een veelgebruikte one-liner-shuffle is arr.sort(() => Math.random() - 0.5). Het ziet er elegant uit, past op één regel en voelt willekeurig — maar de resulterende permutatieverdeling is verre van uniform. De ECMAScript-specificatie vereist dat vergelijkerfuncties deterministisch en transitief zijn: als a < b en b < c, dan a < c. Een kop-of-munt-vergelijker schendt dat contract, en de sorteerimplementatie van de engine bezoekt vergelijkerparen in een niet-uniform patroon.

V8 (Chrome, Node) gebruikt Timsort; oudere engines gebruikten quicksort of mergesort. Elke implementatie bezoekt een andere subset van vergelijkerparen en hergebruikt gecachte vergelijkingen op verschillende manieren. Zoals Mike Bostock empirisch aantoonde (bost.ocks.org/mike/shuffle/compare.html), produceert de naïeve sortering sterk bevooroordeelde uitvoer waarbij sommige permutaties meerdere keren vaker verschijnen dan andere — onacceptabel voor een weggeefactie, een A/B-bucket of elke trekking die eerlijk moet zijn.

Het juiste algoritme is Fisher-Yates (Knuth-shuffle): loop de array van de laatste index tot de eerste en verwissel bij elke stap het huidige element met één uniform willekeurig gekozen uit indices 0..i. Het draait in O(n), gebruikt O(1) extra ruimte en produceert aantoonbaar elke permutatie met gelijke kans.

// 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;
}

// IMPLEMENTEER HET ZELF

Wil je je eigen kiezer bouwen of de onze controleren? Dit zijn de exacte primitieven die deze pagina gebruikt — klein, zonder afhankelijkheden en identiek aan wat in je browser draait wanneer je op PICK klikt. Kopieer ze rechtstreeks naar een Node-script of een andere webapp.

// 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);

>> veelgestelde vragen

V: Hoe wordt de willekeur gegenereerd?

A: Wanneer er geen seed wordt opgegeven, gebruiken we window.crypto.getRandomValues, de cryptografisch veilige willekeurige getallengenerator van de browser op basis van de Web Crypto API. Het is dezelfde entropiebron die wordt gebruikt voor cryptografische sleutels en tokens, dus ruimschoots sterk genoeg voor loterijen en shuffles. Wanneer je een seed opgeeft, leiden we deterministisch een 53-bits toestand af via de cyrb53-string-hash en voeden die in een mulberry32-PRNG, zodat de trekking reproduceerbaar is.

V: Kan ik met een seed twee keer hetzelfde resultaat krijgen?

A: Ja. Typ een willekeurige string in het SEED-veld - een datum, een wedstrijdnaam, een hash, wat dan ook. Dezelfde seed gecombineerd met dezelfde invoerlijst en dezelfde modus levert altijd exact dezelfde uitvoer op. Dit is van onschatbare waarde voor transparante weggeefacties waarbij je de seed vooraf wilt publiceren en deelnemers de trekking wilt laten verifiëren, of voor engineeringrotaties die reproduceerbaar moeten zijn op verschillende machines.

V: Wat is het verschil tussen Pick N en Shuffle?

A: Pick N selecteert een deelverzameling van grootte N uit je lijst, optioneel met teruglegging (duplicaten toegestaan). Shuffle geeft de hele lijst herordend terug - er worden geen items verwijderd of herhaald, alleen herschikt. Gebruik Pick N wanneer je slechts enkele winnaars uit veel inzendingen wilt; gebruik Shuffle wanneer elk item nog moet meedoen maar in een nieuwe willekeurige volgorde, zoals een presentatiewachtrij of afspeellijst.

V: Is array.sort(() => Math.random() - 0.5) echt willekeurig?

A: Nee, het is bevooroordeeld. De ECMAScript-specificatie vereist dat sorteervergelijkers deterministisch en transitief zijn (als a < b en b < c dan a < c). Een willekeurige vergelijker schendt dat contract, en verschillende JavaScript-engines voeren verschillende onderliggende sorteeralgoritmen uit (TimSort, mergesort, quicksort) die op verschillende manieren met de willekeurige vergelijker interageren. Het resultaat is een niet-uniforme verdeling waarbij sommige permutaties veel vaker verschijnen dan andere. Fisher-Yates is het juiste algoritme.

V: Kan ik een lijst met een oneven aantal koppelen?

A: Ja. De Pair Up-modus schudt je lijst met Fisher-Yates en deelt die vervolgens op in de door jou gekozen groepsgrootte. Als het totaal niet gelijkmatig deelbaar is, wordt de laatste groep kleiner en duidelijk gemarkeerd als een restgroep in de uitvoer. Bij een oneven lijst kun je ofwel een losse vermelding accepteren, de groepsgrootte vergroten of vóór de trekking een tijdelijke naam zoals "bye" toevoegen.

V: Wordt mijn lijst ergens geüpload?

A: Nee. Deze tool is 100% client-side - je lijst, je seed en de uitvoer verlaten nooit je browser. Er is geen serveraanroep, geen logging en geen analyse van de invoer. Je kunt na het laden van de pagina de internetverbinding verbreken en elke knop blijft werken. Ververs de pagina en de gegevens zijn voorgoed weg.

V: Is er een visuele rad-versie van deze tool?

A: Voor een interactieve namenrad-ervaring met animatie, geluidseffecten en een grote visuele onthulling, zie randompickerwheel.app. Het is een aanvullend product gericht op live trekkingen voor publiek en schermdelen. Deze pagina is een tekst-eerst kiezer gericht op grote lijsten, reproduceerbare trekkingen met seed, CSV-export en koppelen - handig wanneer je een snel deterministisch antwoord nodig hebt in plaats van een presentatie.

// OTHER LANGUAGES