Blog Dla Programistów C#/.NET

wtorek, 2 września 2025

Słowo "losowość" często kojarzy się z chaosem i nieprzewidywalnością, ale w przypadku komputerów to tylko pozory.

Pisząc programy, często korzystamy z funkcji losujących – choćby do generowania identyfikatora, symulacji rzutu kostką czy losowego wyboru elementu z listy. Ja sam długo żyłem w przekonaniu, że komputer rzeczywiście losuje te liczby. W końcu klasa Random w .NET brzmi jednoznacznie. Jednak szybko odkryłem, że losowość w programowaniu to trochę iluzja. W rzeczywistości nasze aplikacje generują liczby pseudolosowe. Co to znaczy i dlaczego tak jest? To wyjaśnię Ci w tym dzisiejszym artykule.

Czy Komputer Naprawdę Losuje Liczby? Pseudolosowość w Programowaniu

Prawdziwa losowość vs. pseudolosowość


Z punktu widzenia matematyki i informatyki liczba prawdziwie losowa to taka, której nie da się przewidzieć – jest całkowicie przypadkowa. Komputery z natury działają jednak deterministycznie (wykonują zaprogramowane instrukcje krok po kroku), więc nie potrafią generować liczb w pełni losowych. Aby uzyskać prawdziwy przypadek, musiałyby czerpać z jakiegoś zewnętrznego, nieprzewidywalnego zjawiska fizycznego – na przykład szumu radiowego albo rozpadu promieniotwórczego. Takie podejście stosują tzw. hardware RNG (sprzętowe generatory losowości) czy usługi pokroju random.org, ale typowy program tego nie robi.

Zamiast tego używamy generatorów liczb pseudolosowych (ang. PRNG – Pseudo-Random Number Generator). To algorytmy, które produkują ciągi liczb wyglądające na losowe, choć tak naprawdę losowe nie są. Sekwencja pseudolosowa spełnia pewne statystyczne cechy losowości (rozrzut wartości, rozkład), ale jest tworzona w przewidywalny, z góry określony sposób. Innymi słowy: rzeczy generowane przez algorytm są z definicji nielosowe.

Kluczowym pojęciem jest tutaj ziarno (seed). Generator pseudolosowy rozpoczyna pracę od liczby startowej (ziarna) i na tej podstawie deterministycznie wylicza kolejne pozornie przypadkowe wartości. Jeśli algorytm i ziarno są takie same, ciąg wynikowy będzie identyczny. Ta powtarzalność to cecha charakterystyczna pseudolosowości – i zarazem dowód, że nie mamy do czynienia z prawdziwym przypadkiem.


Jak działa Random w C#?


Weźmy na przykład dobrze nam znaną klasę Random z .NET. Dokumentacja Microsoftu wprost stwierdza, że Random to „generator pseudolosowy” – czyli algorytm zwracający ciąg liczb spełniających pewne statystyczne wymagania losowości. Pod spodem .NET wykorzystuje zaawansowany algorytm oraz ziarno. Domyślnie, jeśli sami nie podamy ziarna, .NET sam wybierze wartość startową – w starszych wersjach biblioteki używano np. bieżącego czasu (ticków zegara), obecnie generowane jest losowe ziarno bazowe na potrzeby każdego wątku.

Efekt końcowy jest taki, że za każdym razem gdy wołamy Random.Next(), dostajemy liczbę wyglądającą na losową. Ale: jeśli dwóch różnych użytkowników użyje tego samego algorytmu i tego samego ziarna, otrzymają dokładnie taką samą sekwencję "losowych" liczb. Pokażmy to na prostym przykładzie w C#:

/* Dwa generatory z tym samym ziarnem: */
Random rng1 = new Random(12345);
Random rng2 = new Random(12345);
/* Pierwsze kilka wartości z obu generatorów: */
Console.WriteLine($"{rng1.Next()}, {rng1.Next()}, {rng1.Next()}");
Console.WriteLine($"{rng2.Next()}, {rng2.Next()}, {rng2.Next()}");

Jeśli uruchomisz powyższy kod, obie linie wypiszą identyczny ciąg liczb. To nie magia – po prostu oba obiekty Random startują z ziarnem 12345, więc ich algorytm wylicza te same kolejne wartości. Właśnie dlatego mówimy, że to pseudolosowość.

Warto wiedzieć, że kiepskie użycie generatora może skutkować słabą "losowością". Sam kiedyś zrobiłem błąd, tworząc wiele instancji Random jedna po drugiej. Każda domyślnie inicjowała się na podstawie czasu, więc wywołane w jednej pętli miały niemal ten sam czas – w efekcie wygenerowały powtarzające się wartości. Pamiętaj więc, by nie tworzyć nowego Random za każdym razem (lepiej użyć raz utworzonego lub skorzystać z Random.Shared udostępnionego we współczesnym .NET). Dzięki temu unikniesz sytuacji, gdzie pozorne "losy" zaczynają się powtarzać.


Czy to problem, że Random nie jest naprawdę losowy?


Na szczęście, dla większości zastosowań pseudolosowość w zupełności wystarcza. Sekwencje generowane przez Random są na tyle "pomieszane", że sprawiają wrażenie losowych dla zwykłego odbiorcy. Gdy piszesz grę, symulację czy aplikację biznesową, fakt że liczby są deterministyczne nie ma większego znaczenia – ważne, że są nieprzewidywalne dla użytkownika i statystycznie równomierne. Ba, zaletą pseudolosowości jest właśnie możliwość powtórzenia wyników w razie potrzeby (wystarczy znać ziarno). Dzięki temu np. w testach możemy odtworzyć scenariusze losowe, co byłoby niemożliwe przy prawdziwym chaosie.

Są jednak sytuacje, gdy zwykły generator pseudolosowy to za mało. Klasyczny przykład to bezpieczeństwo i kryptografia. Jeżeli twój program losuje hasło, klucz szyfrujący lub cokolwiek, czego nie może odgadnąć atakujący – wtedy standardowy Random się nie nadaje. Dlaczego? Bo jest przewidywalny – znając algorytm (a ten jest powszechnie znany) i potencjalne ziarno, można próbować odtworzyć wygenerowane liczby. W takich przypadkach trzeba użyć generatora kryptograficznego, który korzysta z trudniejszych do przewidzenia źródeł. W .NET mamy do tego np. klasę RandomNumberGenerator z przestrzeni System.Security.Cryptography, która pobiera losowość z zasobów systemowych (jak ruchy myszy, czasy zdarzeń itp.). To wciąż pseudolosowe liczby, ale o znacznie wyższym poziomie nieprzewidywalności.

Podsumowując: klasa Random w C# (i odpowiedniki w innych językach) nie generuje prawdziwego chaosu – tylko sprytnie udaje losowość za pomocą matematyki. Nie jest to wadą, dopóki rozumiemy ograniczenia. W programowaniu określenie "losowy" zawsze oznacza "pseudolosowy", bo stoi za nim algorytm. Mimo to, dla większości zadań to udawane szczęście nam w zupełności wystarcza.


Podsumowanie


Mam nadzieję, że teraz widzisz, dlaczego Random to liczba pseudolosowa, a nie losowa – i że to nic strasznego. Komputer "losuje" według przepisu, ale robi to na tyle dobrze, że możemy korzystać z tego na co dzień. Ważne, by pamiętać o tej pseudolosowości, zwłaszcza gdy potrzebujemy prawdziwego bezpieczeństwa czy maksymalnej nieprzewidywalności.

Jeśli spodobał Ci się ten artykuł i chcesz wejść jeszcze głębiej w świat C# i .NET, zajrzyj do moich kursów online (https://modestprogrammer.pl/kursy). Krok po kroku uczę różnych tematów (np. AI, ASP.NET, Blazor, Bezpieczeństwo, Własna Aplikacja), prowadzę kursantów od podstaw aż po zaawansowane techniki w C#/.NET, łącząc teorię z praktycznymi projektami, które od razu możesz wykorzystać w pracy.

To wszystkie na dzisiaj. Jeżeli taki artykuł Ci się spodobał, to koniecznie dołącz do mojej społeczności – darmowe zapisy, gdzie będziesz również miał dostęp do dodatkowych materiałów i przede wszystkim bonusów. Do zobaczenia w kolejnym artykule.

Autor artykułu:
Kazimierz Szpin
Kazimierz Szpin
CTO & Founder - FindSolution.pl
Programista C#/.NET. Specjalizuje się w Blazor, ASP.NET Core, ASP.NET MVC, ASP.NET Web API, WPF oraz Windows Forms.
Autor bloga ModestProgrammer.pl
Dodaj komentarz

Wyszukiwarka

© Copyright 2025 modestprogrammer.pl | Sztuczna Inteligencja | Regulamin | Polityka prywatności. Design by Kazimierz Szpin. Wszelkie prawa zastrzeżone.