Dlaczego otrzymuję ten konkretny wzór kolorów podczas używania rand ()?

170

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

Mały kucyk
źródło
26
cos rand isnt rand - ładne demo tego. Jest w 100% deterministyczny
pm100
50
@ pm100: Jak dobrze wyjaśnia odpowiedź Bames53, uzyskasz ten sam wzorzec, nawet jeśli użyjesz idealnego generatora liczb losowych.
TonyK,
13
Przepraszam za pytanie: skoro używasz współrzędnych xiy do obliczania wartości pikseli, dlaczego miałbyś oczekiwać, że te wartości będą niezależne od współrzędnych? Jeśli obraz jest zbyt jednolicie losowy, tego potrzebujesz, prawda?
Thomas Padron-McCarthy
15
Wyciągnięte wnioski: Robienie przypadkowych rzeczy z przypadkowymi liczbami nie daje przypadkowych rezultatów :)
Hagen von Eitzen

Odpowiedzi:

355

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.

uint8_t blue(uint32_t x, uint32_t y) {
    return (rand() % 2)                  ? (x + y) % rand() :
           ((x * y % 1024) % rand()) % 2 ? (x - y) % rand() :
                                           rand();
}

Każda wartość piksela jest wybrany z jednej z trzech funkcji: (x + y) % rand(), (x - y) % rand()i rand();

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”

wprowadź opis obrazu tutaj


  • (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”

wprowadź opis obrazu tutaj


  • (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ż xi ysą 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”

wprowadź opis obrazu tutaj

Każdy piksel końcowego obrazu jest pobierany z jednego z tych trzech obrazów przy użyciu funkcji rand() % 2i ((x * y % 1024) % rand()) % 2. Pierwszy z nich można odczytać jako wybieranie z 50% prawdopodobieństwem (ignorowanie problemów z rand()bitami o najniższej kolejności).

Oto zbliżenie miejsca, w którym rand() % 2jest prawdziwe (białe piksele), więc wybrano Obraz A.

wprowadź opis obrazu tutaj

Druga funkcja ((x * y % 1024) % rand()) % 2znowu ma problem, gdzie rand()jest zwykle większa niż (x * y % 1024)dzielona rzecz , która wynosi co najwyżej 1023. Wtedy (x*y%1024)%2nie 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 %2wartości parzyste w trzech czwartych czasu dadzą 0 w trzech czwartych czasu.

Oto zbliżenie miejsca, w którym ((x * y % 1024) % rand()) % 2jest prawdziwe, aby można było wybrać obraz B. Wybiera dokładnie, gdzie obie współrzędne są nieparzyste.

wprowadź opis obrazu tutaj

A oto zbliżenie miejsca, w którym można wybrać Obraz C:

wprowadź opis obrazu tutaj

Na koniec łącząc warunki, tutaj wybrano Obraz B:

wprowadź opis obrazu tutaj

A gdzie wybrany jest Obraz C:

wprowadź opis obrazu tutaj

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.

bames53
źródło
45

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 + ypodana, że RAND_MAXzwykle 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, randfunkcja nie zwraca prawdziwie losowych liczb i zamiast tego używa funkcji pseudolosowej do tworzenia wartości. Domyślna implementacja randw 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:

Obraz

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.

templatetypedef
źródło