Próbowałem utworzyć plik obrazu, taki jak ten:
uint8_t raw_r[pixel_width][pixel_height];
uint8_t raw_g[pixel_width][pixel_height];
uint8_t raw_b[pixel_width][pixel_height];
uint8_t blue(uint32_t x, uint32_t y)
{
return (rand()%2)? (x+y)%rand() : ((x*y%1024)%rand())%2 ? (x-y)%rand() : rand();
}
uint8_t green(uint32_t x, uint32_t y)
{
return (rand()%2)? (x-y)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
uint8_t red(uint32_t x, uint32_t y)
{
return (rand()%2)? (y-x)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
for (y=0; y<pixel_height; ++y)
{
for (x=0; x<pixel_width; ++x)
{
raw_b[x][y]=blue(x, y);
raw_g[x][y]=green(x, y);
raw_r[x][y]=red(x, y);
}
}
Spodziewałem się, że otrzymam coś losowego (biały szum). Jednak wynik jest interesujący:
Czy znasz powód dlaczego?
Edytować
Teraz jest jasne, że nie ma z tym nic wspólnego rand()
.
Spróbuj również tego kodu:
for (x=0; x<pixel_width; ++x)
for (y=0; y<pixel_height; ++y)
{
r[x][y] = (x+y);
g[x][y] = (y-x);
/* b[x][y] = rand()%2? x : y; */
}
Odpowiedzi:
Początkowo miałem otrzymać taką samą odpowiedź, jak wszyscy inni i przypisać to do problemów
rand()
. Jednak pomyślałem, że lepiej to zrobić i zamiast tego przeanalizowałem rozkład, który faktycznie generuje twoja matematyka.TL; DR: Wzorzec, który widzisz nie ma nic wspólnego z podstawowym generatorem liczb losowych, a zamiast tego jest po prostu spowodowany sposobem, w jaki twój program manipuluje liczbami.
Będę trzymał się twojej niebieskiej funkcji, ponieważ wszystkie są podobne.
Każda wartość piksela jest wybrany z jednej z trzech funkcji:
(x + y) % rand()
,(x - y) % rand()
irand()
;Spójrzmy na obrazy utworzone przez każde z nich samodzielnie.
rand()
Tego można się spodziewać, po prostu hałasu. Nazwij to „Obrazem C”
(x + y) % rand()
Tutaj dodajesz współrzędne pikseli, a resztę bierzesz z dzielenia przez liczbę losową. Jeśli obraz ma rozmiar 1024x1024, to suma mieści się w zakresie [0-2046]. Losowa liczba, według której nurkujesz, mieści się w zakresie [0, RAND_MAX], gdzie RAND_MAX wynosi co najmniej 32 tys., Aw niektórych systemach wynosi 2 miliardy. Innymi słowy, jest co najwyżej 1 na 16 szans, że reszta nie jest po prostu
(x + y)
. Więc w większości ta funkcja po prostu wytworzy gradient rosnącego niebieskiego w kierunku + x + y.Jednak używasz tylko najniższych 8 bitów, ponieważ zwracasz a
uint8_t
, więc będziesz mieć paski gradientów o szerokości 256 pikseli.Nazwij to „Obrazem A”
(x - y) % rand()
Tutaj robisz coś podobnego, ale z odejmowaniem. Dopóki x jest większe niż y, będziesz mieć coś podobnego do poprzedniego obrazu. Ale gdy y jest większe, wynikiem jest bardzo duża liczba, ponieważ
x
iy
są bez znaku (wyniki ujemne zawijają się do początku zakresu typu bez znaku), a następnie% rand()
kopnięcia i faktycznie pojawia się szum.Nazwij to „Obrazem B”
Każdy piksel końcowego obrazu jest pobierany z jednego z tych trzech obrazów przy użyciu funkcji
rand() % 2
i((x * y % 1024) % rand()) % 2
. Pierwszy z nich można odczytać jako wybieranie z 50% prawdopodobieństwem (ignorowanie problemów zrand()
bitami o najniższej kolejności).Oto zbliżenie miejsca, w którym
rand() % 2
jest prawdziwe (białe piksele), więc wybrano Obraz A.Druga funkcja
((x * y % 1024) % rand()) % 2
znowu ma problem, gdzierand()
jest zwykle większa niż(x * y % 1024)
dzielona rzecz , która wynosi co najwyżej 1023. Wtedy(x*y%1024)%2
nie daje równorzędnie 0 i 1. Każda liczba nieparzysta pomnożona przez dowolną liczbę parzystą jest parzysta. Każda liczba parzysta pomnożona przez dowolną liczbę parzystą jest również parzysta. Tylko liczba nieparzysta pomnożona przez liczbę nieparzystą jest nieparzysta, a zatem%2
wartości parzyste w trzech czwartych czasu dadzą 0 w trzech czwartych czasu.Oto zbliżenie miejsca, w którym
((x * y % 1024) % rand()) % 2
jest prawdziwe, aby można było wybrać obraz B. Wybiera dokładnie, gdzie obie współrzędne są nieparzyste.A oto zbliżenie miejsca, w którym można wybrać Obraz C:
Na koniec łącząc warunki, tutaj wybrano Obraz B:
A gdzie wybrany jest Obraz C:
Wynikową kombinację można odczytać jako:
Z prawdopodobieństwem 50% użyj piksela z obrazu A. Reszta czasu wybierz między obrazem B a obrazem C, B, gdzie obie współrzędne są nieparzyste, C, gdzie jedna z nich jest parzysta.
Wreszcie, ponieważ robisz to samo dla trzech różnych kolorów, ale przy różnych orientacjach, wzory są zorientowane inaczej w każdym kolorze i tworzą przecinające się paski lub wzór siatki, który widzisz.
źródło
Wiele obliczeń, które wykonujesz w swoim kodzie, nie prowadzi do prawdziwie losowych wartości. Te ostre linie, które widzisz, odpowiadają miejscom, w których względne wartości twoich współrzędnych xiy wymieniają się ze sobą, a kiedy tak się dzieje, używasz zasadniczo różnych formuł. Na przykład obliczenia
(x + y) % rand()
generalnie zwracają wartośćx + y
, ponieważrand()
(zwykle) zwraca liczbę znacznie, znacznie większą niżx + y
podana, żeRAND_MAX
zwykle jest to dość duża liczba. W tym sensie nie powinieneś oczekiwać powrotu białego szumu, ponieważ algorytm, którego używasz do generowania rzeczy, jest odchylony od generowania białego szumu. Jeśli chcesz mieć biały szum, po prostu ustaw każdy piksel narand()
. Jeśli chcesz mieć ładny wzór, taki jak ten, który masz powyżej, ale z odrobiną losowości wrzucanej tu i tam, nadal używaj napisanego kodu.Ponadto, jak zauważył @ pm100 w komentarzach,
rand
funkcja nie zwraca prawdziwie losowych liczb i zamiast tego używa funkcji pseudolosowej do tworzenia wartości. Domyślna implementacjarand
w wielu systemach wykorzystuje typ generatora liczb pseudolosowych zwany generatorem liniowym kongruencjalnym, który generuje liczby, które w krótkich seriach mogą wydawać się losowe, ale w praktyce są zdecydowanie nielosowe. Na przykład, oto animacja z Wikipedii pokazująca, jak losowe punkty w przestrzeni wybrane za pomocą liniowego generatora kongruencji wpadają w ustaloną liczbę hiperpłaszczyzn:Jeśli zamienisz współrzędne x, y i z współrzędnymi R, G i B, wygląda to niezwykle podobnie do wyniku generowanego przez twój program. Podejrzewam, że prawdopodobnie nie jest to tutaj główna kwestia, ponieważ inny aspekt, o którym mowa powyżej, będzie prawdopodobnie znacznie wyraźniejszy.
Jeśli szukasz liczb losowych o wyższej jakości, musisz użyć losowego źródła o wyższej jakości. W C możesz rozważyć odczytanie bajtów z
/dev/urandom/
(w systemie podobnym do Linuksa), co daje dość jednolite losowe wartości. C ++ ma teraz wiele dobrych prymitywów generowania liczb losowych w swoich standardowych bibliotekach, jeśli są dostępne.źródło