Jak działa losowanie — przykład krok po kroku
Przejdź przez jedno losowanie od początku do końca: 1 000 kwalifikujących się komentarzy, 3 zwycięzców, 9 rezerwowych. Bez żargonu, bez niedomówień.
Scenariusz
Wyobraź sobie, że konkurs dobiegł końca. Komentarze zostały pobrane, filtry konkursu zostały uruchomione i 1 000 zgłoszeń jest kwalifikujących się. Nagroda przyznaje 3 zwycięzców z 9 rezerwowymi, na wypadek gdyby żaden z nich nie był osiągalny. Oto prawdopodobieństwo dla każdego zgłoszenia — identyczne dla każdego z 1 000 kwalifikujących się komentarzy, niezależnie od czasu publikacji czy długości nazwy komentującego.
| Wynik | Szansa dla dowolnego zgłoszenia |
|---|---|
| Wylosowany jako główny zwycięzca (miejsca 1–3) | 3 na 1 000 — 0,30% |
| Wylosowany jako rezerwowy (miejsca 4–12) | 9 na 1 000 — 0,90% |
| Wylosowany na dowolne miejsce zwycięskie | 12 na 1 000 — 1,20% |
Nic się nie zmienia, jeśli konkurs ma 444 komentarzy lub 444 444. Algorytm jest identyczny. Zmienia się tylko mianownik.
Krok 1 — Zbierz i przefiltruj zgłoszenia
Aplikacja pobiera każdy komentarz z posta na Facebook lub Instagram, który podłączyłeś, a następnie stosuje filtry konkursu — duplikaty, zablokowane słowa, zasady dotyczące wieku konta i inne skonfigurowane reguły. To, co przeżyje, stanowi zbiór kwalifikujący się. W naszym scenariuszu jest to 1 000 komentarzy. Każdy ma wiersz w bazie danych z unikalnym numerycznym ID; te 1 000 ID to coś, na czym opiera się reszta losowania.
Krok 2 — Posortuj je w kolejności kanonicznej
Zanim cokolwiek losowego się wydarzy, aplikacja sortuje 1 000 ID w ściśle rosnącej kolejności numerycznej — 17, 142, 203, 1058, 9941, … — i odrzuca kolejność, w jakiej przybyły. Dlaczego? Ponieważ Facebook może zwracać komentarze w różnej kolejności w zależności od paginacji, pory dnia lub zmian w backendie. Gdyby losowanie używało kolejności Facebooka, dwa ponowne pobrania mogłyby dać różnych zwycięzców z tego samego ziarna losowego. Sortowanie najpierw przypina dane wejściowe do jednej kanonicznej listy, dzięki czemu samo ziarno determinuje wynik.
Krok 3 — Zablokuj listę wejściową skrótem SHA-256
Posortowana lista jest wprowadzana do SHA-256 — kryptograficznej funkcji skrótu — co daje 64-znakowy odcisk palca. Odcisk palca, wraz z pełną listą, jest zapisywany w rekordzie konkursu. Od tej chwili każdy sprawdzający konkurs może potwierdzić, że lista kwalifikujących się nie została zmieniona: ponownie zahaszuj opublikowaną listę, porównaj z zapisanym odciskiem palca, a jeden zmieniony ID da zupełnie inny skrót. Nie ma możliwości, aby po tym kroku cicho dodać lub usunąć komentarz bez widocznego śladu.
Krok 4 — Wygeneruj 256-bitowe losowe ziarno
Aplikacja prosi kryptograficzne źródło losowości systemu operacyjnego (SecureRandom.hex(32)) o 32 bajty nieprzewidywalnej losowości i zapisuje wynik jako 64-znakowy ciąg szesnastkowy — na przykład a3f1…ce. 256 bitów oznacza 2²⁵⁶ ≈ 10⁷⁷ możliwych ziaren. Dla perspektywy: obserwowalny wszechświat zawiera około 10⁸⁰ atomów. Nikt — ani twórca konkursu, ani operator, ani napastnik obserwujący z zewnątrz — nie może przewidzieć ani odgadnąć tego ziarna przed jego wygenerowaniem.
Krok 5 — Przetasuj 1 000 ID, a następnie weź pierwsze 12
Ziarno inicjalizuje deterministyczny silnik tasowania, który następnie wykonuje tasowanie Fisher–Yates na 1 000 posortowanych ID. Fisher–Yates to standardowy algorytm uczciwego tasowania — każdy z 1 000 wpisów ma równą szansę znalezienia się na każdej pozycji. Po przetasowaniu aplikacja bierze pierwsze 12 wpisów z wierzchu. To cały krok wyboru: tasuj, wytnij 12.
Krok 6 — Przypisz pozycje
12 wybranych wpisów jest przetwarzanych kolejno. Pierwsze trzy stają się zwycięzcami 1, 2 i 3, a kolejne dziewięć — rezerwowymi 1–9. Pozostałe 988 kwalifikujących się wpisów jest rejestrowanych jako niezaznaczone. Komentarze wcześniej odfiltrowane są oznaczane jako zdyskwalifikowane z podaniem konkretnego powodu niekwalifikowania się.
Krok 7 — Opublikuj wszystko
Na koniec aplikacja zapisuje pięć dowodów w rekordzie konkursu — wszystkie publicznie dostępne. Bez nich wynik byłby czarną skrzynką. Z nimi każdy może odtworzyć całe losowanie w domu i potwierdzić, że opublikowani zwycięzcy są jedynymi zwycięzcami, jakich ziarno mogło wyprodukować.
Co jest publikowane przy każdym losowaniu
Każdy z poniższych elementów jest przechowywany w konkursie i udostępniany za pośrednictwem publicznych stron weryfikacji. Razem sprawiają, że losowanie jest w pełni odtwarzalne — te same dane wejściowe, te same dane wyjściowe, zawsze.
- 256-bitowe losowe ziarno — 64-znakowy ciąg szesnastkowy z kroku 4.
- Posortowana lista kwalifikujących się ID — wszystkie 1 000 ID z kroku 2 w kolejności kanonicznej.
- Odcisk palca SHA-256 ID — 64-znakowy skrót z kroku 3. Potwierdza, że opublikowana lista nie została naruszona.
- Wersja algorytmu — aktualnie
v1. Przypina dokładny algorytm wyboru, aby przyszłe ulepszenia nigdy nie zmieniały wstecznie starych wyników. - Wersja Ruby — przypina środowisko uruchomieniowe języka, ponieważ dokładna kolejność tasowania od niego zależy.
Powtórz obliczenia samodzielnie — w pięciu liniach
Jeśli masz zainstalowane Ruby (jest dostarczone z macOS i większością dystrybucji Linux), możesz odtworzyć dokładne losowanie w domu. Każdy konkurs publikuje własny plik eligible-ids.txt na swojej stronie weryfikacji (wzorzec URL: /results/<token>/verify) — przewodnik weryfikacji przeprowadza krok po kroku przez jego pobieranie. Skopiuj opublikowane ziarno i wklej to do 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)
12 ID wychodzi w dokładnie tej samej kolejności, co opublikował konkurs. Trzej zwycięzcy, dziewięciu rezerwowych, bit po bicie identycznie. To właśnie sprawia, że losowanie Pick a Winner jest weryfikowalne zamiast jedynie obiecywanego — matematyka wykonuje pracę, a nie słowo operatora.
Dlaczego to trudno sfałszować
- Ziarno jest niemożliwe do odgadnięcia. 2²⁵⁶ możliwości, generowane dopiero po zatwierdzeniu listy kwalifikujących się. Nikt nie może wstępnie obliczyć, którym ID ziarno by sprzyjało, bo ziarno nie istnieje aż do momentu losowania.
- Dane wejściowe są zablokowane. Odcisk palca SHA-256 oznacza, że nie możesz po cichu dodać komentarza znajomego po zobaczeniu ziarna — odcisk palca by się zmienił i losowanie nie dałoby się już odtworzyć.
- Tasowanie jest mechaniczne. Gdy ziarno i posortowana lista istnieją, zwycięzcy są już zdeterminowani. Żadna ludzka decyzja nie stoi między nimi. Każda późniejsza modyfikacja wyszłaby na jaw jako niezgodność w momencie, gdy ktokolwiek powtórzy obliczenia.
- Dowód jest publiczny. Ziarno, lista, skrót, wersja algorytmu, wersja Ruby — wszystkie pięć jest opublikowanych. Nie ma żadnej prywatnej części losowania, którą operator mógłby zmienić bez tego, żeby każdy mógł to zobaczyć.