Rand () kullanırken neden bu renk desenini alıyorum?


170

Böyle bir görüntü dosyası oluşturmaya çalıştım:

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);
    }
}

Rastgele bir şey almayı bekledim (beyaz gürültü). Ancak, çıktı ilginçtir:

Sebebini biliyor musun?


Düzenle

Şimdi bunun hiçbir ilgisi olmadığı açık rand().

Ayrıca bu kodu deneyin:

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


26
çünkü rand değil - güzel bir demo. % 100 deterministik
pm100


50
@ pm100: bames53'ün cevabı çok iyi açıkladığı gibi, mükemmel bir rasgele sayı üreteci kullansanız bile aynı modeli elde edersiniz.
TonyK

13
Bir soruyu özür dilerim: Piksel değerlerini hesaplamak için x ve y koordinatlarını kullandığınız için, neden bu değerlerin koordinatlardan bağımsız olmasını beklersiniz? Eğer görüntü eşit olarak rastgele görünüyorsa, ihtiyacınız olan şey budur, değil mi?
Thomas Padron-McCarthy

15
Öğrenilen dersler: Rasgele sayılarla rastgele şeyler yapmak rastgele sonuçlar vermez :)
Hagen von Eitzen

Yanıtlar:


355

Başlangıçta herkesle aynı cevaba sahip olacaktım ve bu konulara tebeşir koyacaktım rand(). Ancak, bunu yapmayı daha iyi düşündüm ve bunun yerine matematiğinizin gerçekte ürettiği dağılımı analiz ettim.

TL; DR: Gördüğünüz kalıbın altında yatan rasgele sayı üreteci ile bir ilgisi yoktur ve bunun yerine programınızın sayıları işleme biçiminden kaynaklanmaktadır.

Hepsi benzer olduğu için mavi işlevinize bağlı kalacağım.

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

Her bir piksel değerinin üç fonksiyon birinden seçilir: (x + y) % rand(), (x - y) % rand()ve rand();

Bunların her biri tarafından üretilen görüntülere bakalım.

  • rand()

Bu beklediğiniz, sadece gürültü. Buna "Resim C" deyin

resim açıklamasını buraya girin


  • (x + y) % rand()

Burada piksel koordinatlarını bir araya getiriyorsunuz ve kalanını rastgele bir sayıya bölmekten alıyorsunuz. Görüntü 1024x1024 ise toplam [0-2046] aralığındadır. Dalış yaptığınız rasgele sayı [0, RAND_MAX] aralığındadır; burada RAND_MAX en az 32k ve bazı sistemlerde 2 milyardır. Başka bir deyişle, geri kalanın sadece(x + y) . Yani çoğunlukla bu fonksiyon sadece + x + y yönüne doğru artan mavi bir gradyan üretecektir.

Ancak yalnızca en düşük 8 biti kullanıyorsunuz çünkü uint8_t değerini 256 piksel genişliğinde degrade şeritleriniz olacak.

Buna "Resim A" deyin

resim açıklamasını buraya girin


  • (x - y) % rand()

Burada benzer bir şey yaparsınız, ancak çıkarma ile. X y'den büyük olduğu sürece önceki görüntüye benzer bir şey elde edersiniz. Ancak y'nin daha büyük olduğu yerde sonuç çok büyük bir sayıdır, çünkü xvey imzalanmamıştır (negatif sonuçlar imzalanmamış türün aralığının üstüne sarılır) ve sonra % rand()vurur ve aslında gürültü alırsınız.

Buna "Resim B" deyin

resim açıklamasını buraya girin

Son görüntüdeki her bir piksel işlevlerini kullanarak bu üç resimlerden birini alınır rand() % 2ve ((x * y % 1024) % rand()) % 2. Bunlardan ilki,% 50 olasılıkla seçim olarak okunabilir (sorunları rand()ve düşük dereceli bitlerini göz ardı ederek ).

İşte rand() % 2doğru olanın bir örneği (beyaz pikseller), böylece Resim A seçildi.

resim açıklamasını buraya girin

İkinci fonksiyon , genellikle bölündüğünüzden daha büyük , en fazla 1023 olan bir ((x * y % 1024) % rand()) % 2soruna sahiptir.rand()(x * y % 1024)(x*y%1024)%2 0 ve 1'i eşit sıklıkta üretmez. Herhangi bir tek sayının herhangi bir çift sayıyla çarpımı çifttir. Herhangi bir çift sayının herhangi bir çift sayıyla çarpımı da çifttir. Sadece tek bir sayının tek bir sayıyla çarpımı tuhaftır ve bu nedenle %2zamanın dörtte üçü olan değerler zamanın dörtte üçünü üretecektir.

((x * y % 1024) % rand()) % 2Resim B'nin seçilebilmesi için nerede doğru olduğunun bir örneği. Her iki koordinatın tam olarak nerede olacağını seçiyor.

resim açıklamasını buraya girin

Ve burada Resim C'nin nerede seçilebileceğinin bir örneği:

resim açıklamasını buraya girin

Son olarak, Resim B'nin seçildiği koşullar şöyledir:

resim açıklamasını buraya girin

Resim C'nin seçildiği yer:

resim açıklamasını buraya girin

Ortaya çıkan kombinasyon şu şekilde okunabilir:

% 50 olasılıkla Resim A'daki pikseli kullanın. Kalan süre, her iki koordinatın tek olduğu Resim B ile Resim C, B arasında seçim yapın, her ikisinin de eşit olduğu C.

Son olarak, üç farklı renk için aynı şeyi yaptığınızdan, ancak farklı yönelimlerle, desenler her renkte farklı şekilde yönlendirilir ve gördüğünüz geçiş şeritlerini veya ızgara desenini üretir.


45

Kodunuzda yaptığınız hesaplamaların çoğu gerçekten rastgele değerlere yol açmaz. Gördüğünüz bu keskin çizgiler, x ve y koordinatlarınızın göreli değerlerinin birbiriyle ticaret yaptığı yerlere karşılık gelir ve bu olduğunda temelde farklı formüller kullanırsınız. Örneğin, bilgisayar (x + y) % rand()genellikle değeri geri verecektir x + y, çünkü rand()(genellikle) bir sayıdan çok, çok daha büyük bir sayı döndürür . Yukarıdakine benzer güzel bir desen istiyorsanız, ancak burada ve orada rastgele bir miktar rastgele atmak istiyorsanız, yazdığınız kodu kullanmaya devam edin.x + y göz önüne alındığında RAND_MAXgenellikle oldukça büyük bir sayıdır. Bu anlamda, beyaz paraziti geri almayı beklememelisiniz, çünkü bir şeyleri üretmek için kullandığınız algoritma beyaz gürültü üretmekten uzaktır. Beyaz gürültü istiyorsanız, her pikselirand()

Ayrıca, @ pm100'ün yorumlarda belirttiği gibi, randişlev gerçekten rastgele sayılar döndürmez ve bunun yerine değerleri üretmek için bir sahte işlev kullanır. randBirçok sistemde varsayılan uygulama, kısa patlamalarda rastgele görünebilen, ancak pratikte kesin olarak rasgele olmayan sayılar üreten doğrusal bir kongruratif jeneratör olarak adlandırılan bir tür yalancı sayı üreteci kullanır . Örneğin, Wikipedia'dan, doğrusal bir uyumlu üreteci ile seçilen uzayda rastgele noktaların sabit sayıda hiperplanta nasıl düştüğünü gösteren bir animasyon:

Görüntü

X, y ve z koordinatlarını R, G ve B koordinatlarıyla değiştirirseniz, bu , programınız tarafından üretilen çıktıya oldukça benzer görünür . Bunun muhtemelen buradaki temel sorun olmadığından şüpheleniyorum, çünkü yukarıda bahsedilen diğer husus muhtemelen çok daha belirgin olacaktır.

Daha yüksek kalitede rasgele sayılar arıyorsanız, daha yüksek kalitede rasgele bir kaynak kullanmanız gerekir. C'de, /dev/urandom/(Linux benzeri bir sistemde) gelen ve oldukça rastgele değerler veren baytları okumayı düşünebilirsiniz . C ++ artık sizin için uygunsa, standart kitaplıklarında iyi rasgele sayı üretme ilkellerine sahiptir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.