Hoe een trekking werkt — een uitgewerkt voorbeeld
Loop een echte trekking stap voor stap door: 1.000 in aanmerking komende reacties, 3 winnaars, 9 reserves. Geen jargon, geen vaagheden.
Het scenario
Stel dat de wedstrijd voorbij is. Reacties zijn opgehaald, de filters van de wedstrijd zijn uitgevoerd en 1.000 inzendingen komen in aanmerking. De prijs wordt verdeeld over 3 winnaars met 9 reserves, voor het geval een van de winnaars niet bereikbaar is. Hier is de kans per inzending — identiek voor elk van de 1.000 in aanmerking komende reacties, ongeacht wanneer deze is geplaatst of hoe lang de naam van de reageerder is.
| Uitkomst | Kans voor elke inzending |
|---|---|
| Getrokken als hoofdwinnaar (posities 1–3) | 3 op 1.000 — 0,30% |
| Getrokken als reserve (posities 4–12) | 9 op 1.000 — 0,90% |
| Getrokken in een willekeurige winnende positie | 12 op 1.000 — 1,20% |
Dit verandert niet als de wedstrijd 444 reacties of 444.444 reacties heeft. Het algoritme is identiek. Alleen de noemer verandert.
Stap 1 — De inzendingen verzamelen en filteren
De app haalt elke reactie op het Facebook- of Instagram-bericht op dat je hebt gekoppeld, en past vervolgens de filters van de wedstrijd toe — duplicaten, geblokkeerde woorden, leeftijdsregels voor accounts, wat er ook is ingesteld. Wat overblijft is de groep in aanmerking komende inzendingen. In ons scenario zijn dat 1.000 reacties. Elke reactie heeft een databaserij met een uniek numeriek ID; die 1.000 ID's zijn waarop de rest van de trekking opereert.
Stap 2 — Ze sorteren in een canonieke volgorde
Voordat er iets willekeurigs gebeurt, sorteert de app de 1.000 ID's in strikte oplopende numerieke volgorde — 17, 142, 203, 1058, 9941, … — en verwijdert de volgorde waarin ze zijn binnengekomen. Waarom? Omdat Facebook reacties in verschillende volgorden kan teruggeven afhankelijk van paginering, tijdstip van de dag of back-endwijzigingen. Als de trekking de volgorde van Facebook zou gebruiken, kunnen twee ophaalpogingen met hetzelfde willekeurige zaad verschillende winnaars opleveren. Door eerst te sorteren wordt de invoer vastgezet op één canonieke lijst, zodat het zaad alleen de uitkomst bepaalt.
Stap 3 — De invoerlijst vergrendelen met een SHA-256-hash
De gesorteerde lijst wordt ingevoerd in SHA-256 — een cryptografische hashfunctie — en produceert een vingerafdruk van 64 tekens. De vingerafdruk wordt samen met de volledige lijst opgeslagen in het wedstrijdrecord. Vanaf dit moment kan iedereen die de wedstrijd inspecteert bevestigen dat de in aanmerking komende lijst niet is gewijzigd: herbereken de hash van de gepubliceerde lijst, vergelijk met de opgeslagen vingerafdruk en één gewijzigd ID geeft een volledig andere hash. Er is geen manier om na deze stap stilletjes een reactie toe te voegen of te verwijderen zonder dat dit zichtbaar wordt.
Stap 4 — Een willekeurig zaad van 256 bits genereren
De app vraagt de cryptografisch willekeurige bron van het besturingssysteem (SecureRandom.hex(32)) om 32 bytes onvoorspelbare willekeurigheid en slaat het resultaat op als een hexadecimale tekenreeks van 64 tekens — bijvoorbeeld a3f1…ce. 256 bits betekent 2²⁵⁶ ≈ 10⁷⁷ mogelijke zaden. Ter vergelijking: het waarneembare universum bevat ongeveer 10⁸⁰ atomen. Niemand — niet de wedstrijdmaker, niet de beheerder, niet een aanvaller die van buitenaf meekijkt — kan dit zaad voorspellen of raden voordat het wordt gegenereerd.
Stap 5 — De 1.000 ID's schudden, dan de eerste 12 nemen
Het zaad initialiseert een deterministisch schudalgoritme, dat vervolgens een Fisher-Yates-shuffle uitvoert op de 1.000 gesorteerde ID's. Fisher-Yates is het standaard eerlijke schudalgoritme — elke inzending heeft een gelijke kans om op elke positie terecht te komen. Na het schudden neemt de app de eerste 12 inzendingen van het begin. Dat is de volledige selectiestap: schud, neem 12.
Stap 6 — Posities toewijzen
De 12 geselecteerde inzendingen worden op volgorde doorlopen. De eerste drie worden winnaars 1, 2 en 3, en de volgende negen worden reserves 1 tot en met 9. De overige 988 in aanmerking komende inzendingen worden geregistreerd als niet-geselecteerd. Reacties die eerder zijn gefilterd, worden gemarkeerd als gediskwalificeerd met de specifieke reden waarom ze niet in aanmerking kwamen.
Stap 7 — Alles publiceren
Ten slotte slaat de app vijf bewijsstukken op in het wedstrijdrecord — allemaal openbaar leesbaar. Zonder hen zou het resultaat een zwarte doos zijn. Met hen kan iedereen de volledige trekking thuis reproduceren en bevestigen dat de gepubliceerde winnaars de enige winnaars zijn die het zaad had kunnen opleveren.
Wat er bij elke trekking wordt gepubliceerd
Elk van deze wordt opgeslagen bij de wedstrijd en beschikbaar gesteld via de openbare verificatiepagina's. Samen maken ze de trekking volledig reproduceerbaar — dezelfde invoer, dezelfde uitvoer, altijd.
- Het willekeurige zaad van 256 bits — de hexadecimale tekenreeks van 64 tekens uit stap 4.
- De gesorteerde lijst met in aanmerking komende ID's — alle 1.000 ID's uit stap 2, in canonieke volgorde.
- De SHA-256-vingerafdruk van de ID's — de hash van 64 tekens uit stap 3. Bevestigt dat de gepubliceerde lijst niet is gemanipuleerd.
- De algoritmeversie — momenteel
v1. Legt het exacte selectie-algoritme vast, zodat toekomstige verbeteringen oude resultaten nooit retroactief wijzigen. - De Ruby-versie — legt de taalruntime vast, omdat de exacte volgorde van het schudden ervan afhankelijk is.
Doe de berekening zelf — in vijf regels
Als je Ruby hebt geïnstalleerd (het wordt meegeleverd met macOS en de meeste Linux-distributies), kun je de exacte trekking thuis reproduceren. Elke wedstrijd publiceert zijn eigen eligible-ids.txt op zijn verificatiepagina (URL-patroon: /results/<token>/verify) — de verificatiehandleiding legt stap voor stap uit hoe je het kunt downloaden. Kopieer het gepubliceerde zaad en plak dit in irb:
require "digest"
seed = "PASTE_THE_PUBLISHED_SEED_HERE"
ids = File.read("eligible-ids.txt").split("\n").map(&:to_i).sort
# (1) Sanity check the input: must equal the published SHA-256.
Digest::SHA256.hexdigest(ids.join("\n"))
# (2) Re-run the shuffle. First 3 items are winners 1..3 (in order);
# the next 9 items are reserves 1..9.
ids.shuffle(random: Random.new(seed.to_i(16))).first(12)
De 12 ID's komen in exact dezelfde volgorde tevoorschijn als de wedstrijd heeft gepubliceerd. Drie winnaars, negen reserves, bit voor bit identiek. Dat is wat een Pick a Winner-trekking verifieerbaar maakt in plaats van slechts beloofd — de wiskunde doet het werk, niet het woord van de beheerder.
Waarom dit moeilijk te manipuleren is
- Het zaad is niet te raden. 2²⁵⁶ mogelijkheden, gegenereerd pas nadat de in aanmerking komende lijst is vastgelegd. Niemand kan vooraf berekenen welke ID's het zaad zou bevoordelen, omdat het zaad pas bestaat op het moment van de trekking.
- De invoer is vergrendeld. De SHA-256-vingerafdruk betekent dat je niet stilletjes een reactie van een vriend kunt toevoegen nadat je het zaad hebt gezien — de vingerafdruk zou veranderen en de trekking zou niet meer reproduceerbaar zijn.
- Het schudden is mechanisch. Zodra het zaad en de gesorteerde lijst bestaan, zijn de winnaars bepaald. Er is geen menselijke beslissing tussen hen. Elke achterafse aanpassing zou opvallen zodra iemand de berekening herhaalt.
- Het bewijs is openbaar. Zaad, lijst, hash, algoritmeversie, Ruby-versie — alle vijf worden gepubliceerd. Er is geen privéonderdeel van de trekking dat de beheerder zou kunnen wijzigen zonder dat iedereen het kan zien.