Tohum bazlı rastgele gürültü


16

Şu anda bir pikselin 'koordinatlarına' dayalı bir ekranda rastgele gürültü üretmesi gereken bir program üzerinde çalışıyorum. Programı her yeniden başlattığınızda koordinatlar aynı renge sahip olmalıdır. Ancak, Java'nın util.Random kullanarak, elde ettiğim sonuçlar istediğim kadar rastgele değildir:

ekran görüntüsü

Eğer kombine koordinatları kullanırsam (birbirinin yanındaki her iki koordinattan oluşan bir tamsayıda olduğu gibi) her koordinatın farklı bir sayıya sahip olacağını düşündüm. Bu sayıyı bir tohum olarak kullanarak, bu koordinatın rgb değeri için kullanılacak her bir koordinat için farklı rastgele sayılar elde etmeyi bekledim.

Bu kullandığım kod:

public class Generate {

static Random Random;

    public static int TileColor(int x, int y){          
        Random = new Random(Integer.valueOf(Integer.toString(x)+Integer.toString(y)));
        int b = 1 + Random.nextInt(50);
        int g = 1 + Random.nextInt(50);
        int r = 1 + Random.nextInt(50);
        int color = -Color.rgb888(r, g, b);
        return color;
    }
}

Java'nın Rastgele işlevinin çalışması nedeniyle programın oluşturduğu desen mi yoksa yanlış bir şey mi yapıyorum ve farklı bir yaklaşım denemeliyim?

Güncelleme: Şimdi aşağıdaki kodu kullanarak bitiştirmeyi çevreleyen sorunlardan kurtulmaya çalıştım:

public static int TileColor(int x, int y){  
            Randomy = new Random(y);
            Randomx = new Random(x);
            Random = new Random(Integer.valueOf(Integer.toString(Randomx.nextInt(1234))+Integer.toString(Randomy.nextInt(1234))));
            int b = 1 + Random.nextInt(100);
            int g = 1 + Random.nextInt(100);
            int r = 1 + Random.nextInt(100);
            int color = -Color.rgb888(r, g, b);
            return color;
}

Her nasılsa, bu da (bence) yeterince rastgele bir görüntü sağladı:

mew görüntü

Ancak bu kod piksel başına üç kez yeniden boyutlandırılır. Bu benim için bir sorun olmasa da, daha sonra daha iyi performansa ihtiyacım olması durumunda bu kodu değiştirmeyi düşünürüm.


3
Java'nın Rastgele hakkında emin değilim ama onun gerçek rastgele değil eminim ... en.wikipedia.org/wiki/Pseudorandom_number_generator Bu desenleri neden gördüğünüzü anlayacaksınız.
Salketer

23
Diğer cevaplarda eksik olan çok önemli bir şey: RNG'yi her piksel için yeniden düzenlemeyin. Bir kez tohumlayın ve buna dayanarak görüntünüzdeki tüm pikseller için ardışık değerler oluşturun.
Konrad Rudolph

4
Not: bir sahte sayı üreteci bir boyutta eşit olarak dağıtılabilir , ancak birden fazla boyut kullanılırken başarısız olabilir ... 3B'de etkin bir şekilde noktalar oluşturursunuz (r, g ve b ve 3 farklı koordinat), sadece ürettiği değerlerin muntazam bir şekilde dağıtılmasını değil, ürettiği üçüzlerin de 3D uzayda muntazam bir şekilde dağıtıldığını garanti eder.
Bakuriu

6
@Bakuriu X, Y ve Z bağımsız tekdüze rasgele değişkenler ise, eminim (X, Y, Z) 3B alanda tekdüze.
Jack M

2
Mersenne Twister gibi farklı RNG'leri kullanmayı deneyebilirsiniz .
Kevin

Yanıtlar:


21

Java'nın java.util.Randomsınıfı genellikle oyun 1'de kullanım için yeterince iyi olan sahte sayı dizileri verir . Bununla birlikte, bu özellik sadece bir tohuma dayanan bir dizi çoklu numune için geçerlidir. RNG'yi artan tohum değerleri ile yeniden başlattığınızda ve her dizinin sadece ilk değerine baktığınızda, rastgelelik özellikleri neredeyse iyi olmayacaktır.

Bunun yerine ne yapabilirsiniz:

  • Bir kerede tam piksel parçaları oluşturmak için aynı çekirdeği kullanın. Örneğin, 425: 487 pikselinin renk değerine ihtiyacınız olduğunda, 400: 400 koordinatlarını RNG'ye besleyin, 10000 rastgele renk oluşturun ve 2587 dizininde (25 * 100 + 87) rengi kullanın. Bu şekilde üretilen topaklar, o parçanın her bir pikseli için bu 10000 rastgele rengi yeniden üretmemek için önbelleğe alınmalıdır.
  • Rastgele sayı üreteci kullanmak yerine, koordinat çiftini renk değerine dönüştürmek için mesaj özeti işlevini kullanın. Çoğu MDF'nin çıktısı, rastgele testlerin çoğunu yerine getirecek kadar tahmin edilemez. Çıkış genellikle bir RGB değeri için ihtiyacınız olan 24 bit'ten daha fazladır, ancak bunları kesmek genellikle sorun değildir.

    Performansı artırmak için mesaj özeti oluşturmayı parçalar ile birleştirebilirsiniz. Özet işlevinizin bir çıktısının tam uzunluğunu kullanabilecek kadar büyük küçük piksel parçaları oluşturun.

1 Kimsenin bir sonraki sayıyı tahmin edememesi kesinlikle gerekli olduğunda, daha yavaş ama daha az tahmin edilebilir kullanınjava.security.SecureRandom


13

Programı yeniden başlattığınız herkesin koordinatları aynı renkte olmalıdır

Bu durumda, Perlin gürültüsü veya tek yönlü gürültü gibi deterministik bir gürültü işlevi kullanmak istersiniz .

( Bazı güzel resimlerle Perlin gürültüsü hakkında daha fazla bilgi için bu soruya bakın. )

Çoğunlukla, yerleşik random()veya benzer bir işlevi kullanmak, programı her çalıştırdığınızda size farklı değerler verecektir, çünkü saati bir giriş veya başka bir sözde değer olarak kullanabilirler.

Başka bir seçenek de çevrimdışı olarak bir kez "gürültü haritası" oluşturmak ve daha sonra rastgele sayı kaynağınız olarak kullanmaktır.

Uygulamanızda, x ve öğesinin dize temsillerini birleştiriyorsunuz y. Alan adında benzersiz olmadığı için bu kötü. Örneğin,

x    y   concatenated
40   20  4020
402   0  4020
10   10  1010
101   0  1010
12   34  1234
123   4  1234
1   234  1234

İyi şanslar!


1
Birleştirilmiş numaralar hakkında iyi bir nokta. Ancak programı birden çok kez çalıştırırsam program her zaman aynı sonucu verir. Ben perlin / simpleks gürültü de düşündüm, bir göz olabilir ve daha iyi çalışır görmek. Bununla birlikte, Java'nın neden desen oluşturduğundan henüz emin değilim, çünkü birleştirme sorunu bunu tamamen çözmüyor gibi görünüyor
dragonfly

1
Pikselleri oluşturmadan önce Rastgele sabit bir tohum değeriyle tohumlamak yeterli değil mi?
Jack M

1
@JackM Bu tamamen oyundaki PRNG algoritmasına bağlıdır.
3Dave

4
"Bir keresinde her değeri bir sonraki değer için bir tohum olarak kullanan bir rand () uygulaması gördüm." Çoğu kriptografik olmayan yalancı sayı üreteci böyle çalışmaz mı? Bir sonraki rasgele sayıyı / durumu oluşturmak için girdi olarak önceki rasgele sayıyı (veya durumu) kullanırlar.
JAB

2
@DavidLively İçsel durumları ürettikleri sayı aralığından (örn. Mersenne bükücüleri) daha büyük olmadıkça ve hatta o zaman bile rastgele sayılar dizisi tamamen tohum tarafından belirlenmedikçe, hemen hemen tüm PRNG'ler bunu veya eşdeğer bir şeyi yaparlar.
Konrad Rudolph

9

Tam olarak ne yaptığınızı görelim:

  • Tüm piksellerde birer birer döngü oluşturuyorsunuz
  • Her piksel için koordinatlarının birleşimini tohum olarak kullanırsınız
  • Daha sonra verilen tohumdan yeni bir rastgele başlatırsınız ve 3 sayı alırsınız

Tüm bunlar iyi görünüyor, ancak bir desen alıyorsunuz çünkü:

1,11'deki piksel ve 11,1'deki pikselin her ikisi de 111 numarasına ekilir, böylece aynı renge sahip olduklarından emin olurlar.

Ayrıca, her zaman aynı şekilde döngü yaptığınız sürece, sadece bir jeneratör kullanabilirsiniz, her piksel için bir tane kullanmaya gerek yoktur. Tüm görüntü için bir tane yapacak! Sözde rastlantısallık nedeniyle hala bir çeşit patern olacaktır. @David_Lively bazı Gürültü algoritmaları kullanma konusunda doğru, daha rastgele görünmesini sağlayacaktır.


Mesele, görüntünün görüşünün değişebilmesi gerektiğidir (pozitif koordinatlara ek olarak). Yani bu yaklaşım tamamen işe yaramayacak
yusufçuk

4
Aslında “tüm bunlar” pek iyi gelmiyor - tohumun kendisi kriptografik bir RNG'den olmadığı sürece her piksel için deterministik bir RNG'yi yeniden üretmek korkunç bir stratejidir (ve hatta o zaman dağıtımla ilgili olmayan nedenler için sakat bir stratejidir).
Konrad Rudolph

bu bağlamdaki sayıları doğru bir şekilde birleştirmenin yolunu ekleyebilirsiniz. Yani. (x + y * genişlik)
Taemyr

1

Bir renk üreticisi yapın, ardından karonuz için renklerinizi üretin. Sadece bir kez tohum! En azından karo başına bundan daha fazlasını tohumlamanıza gerek yok.

public class RandomColorGenerator {
  private final int minValue;
  private final int range;
  private final Random random;
  public RandomColorGenerator(int minValue, int maxValue, Random random) {
    if (minValue > maxValue || (long)maxValue - (long)minValue > (long)Integer.MAX_VALUE) {
      throw new IllegalArgumentException();
    }
    this.minValue = minValue;
    this.range = maxValue - minValue + 1;
    this.random = Objects.requireNonNull(random);
  }

  public int nextColor() {
    int r = minValue + random.nextInt(range);
    int g = minValue + random.nextInt(range);
    int b = minValue + random.nextInt(range);
    return -Color.rgb888(r, g, b);
  }
}

public class Tile {
  private final int[][] colors;
  public Tile(int width, int height, RandomColorGenerator colorGenerator) {
    this.colors = new int[width][height];
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        this.colors[x][y] = colorGenerator.nextColor();
      }
    }
  }

  public int getColor(int x, int y) {
    return colors[x][y];
  }
}

Ve kullanım aşağıdaki gibi olacaktır:

RandomColorGenerator generator = new RandomColorGenerator(1, 100, new Random(0xcafebabe));
Tile tile = new Tile(300, 200, generator);
...
// getting the color for x, y:
tile.getColor(x, y);

Eğer sonuçtan memnun değilseniz sadece Randomtohum değiştirin . Ayrıca, tüm istemcilerin aynı görüntüye sahip olması için yalnızca tohum ve boyutları depolamanız / bildirmeniz gerekir.


1

Rasgele kullanmak yerine, MD5 gibi bir özet özeti kullanmayı düşünün. Belirli bir girdiye dayanarak 'rastgele' bir değer öngörmek zor, ancak aynı girdi için her zaman aynı değer sağlar.

Misal:

public static int TileColor(int x, int y){
        final MessageDigest md = MessageDigest.getInstance("MD5");
        final ByteBuffer b = ByteBuffer.allocate(8);
        b.putInt(x).putInt(y);
        final byte[] digest = md.digest(b.array());
        return -Color.rgb888(digest[0], digest[1], digest[2]);
}

NOT: Color.rgb888 (..) 'in nereden geldiğini bilmiyorum, bu yüzden izin verilen aralığın ne olduğunu bilmiyorum. 0-255 normaldir.

Dikkate alınacak iyileştirmeler:

  • Performansı artırmak için MessageDigest ve ByteBuffer değişkenlerini sınıfın dışında yapın. Bunu yapmak için ByteBuffer'ı sıfırlamanız gerekecek ve yöntem artık İş Parçacığı için güvenli olmayacak.
  • Özet dizisi 0-255 bayt değerleri içerecektir, başka aralıklar istiyorsanız, bunlarda biraz matematik yapmanız gerekecektir.
  • Farklı 'rastgele' sonuçlar istiyorsanız, bir çeşit 'tohum' ekleyebilirsiniz. Örneğin, ByteBuffer.allocate (12) olarak değiştirin ve bir .putInt (tohum) ekleyin.

1

Diğerleri, istediğiniz davranışı elde etmenin bir yolunun, "ileti özeti işlevi" olarak adlandırılan bir karma işlevi kullanmak olduğunu belirtmiştir. Sorun, bunların genellikle kriptografik olarak güvenli (yani gerçekten, gerçekten, gerçekten rastgele) ama çok yavaş olan MD5 gibi algoritmalara dayanmasıdır. Rastgele bir piksele her ihtiyacınız olduğunda bir şifreleme karma işlevi kullanırsanız, oldukça ciddi performans sorunlarıyla karşılaşırsınız.

Bununla birlikte, aynı zamanda hızlı olmakla birlikte, amacınız için yeterince rastgele değerler üretebilen kriptografik olmayan karma işlevler vardır. Genellikle ulaştığım şey üfürümdür . Java kullanıcısı değilim ama en az bir Java uygulaması var gibi görünüyor . Her bir pikseli, bir kerede hepsini oluşturmak ve bir dokuda saklamak yerine, koordinatlarından oluşturmanız gerektiğini fark ederseniz, bunu yapmak için iyi bir yol olacaktır.


1

2000'den fazla bir asal kullanırım (maksimum tipik çözünürlük)
Bu, kopya tohumları en aza indirir (veya ortadan kaldırır)

public class Generate {

    static Random Random;

    public static int TileColor(int x, int y){          
        Random = new Random(x + 2213 * y);
        int b = 1 + Random.nextInt(50);
        int g = 1 + Random.nextInt(50);
        int r = 1 + Random.nextInt(50);
        int color = -Color.rgb888(r, g, b);
        return color;
    }
}

0

Randomyeterince rasgele. Bunu iki ana nedenden dolayı yanlış kullanıyorsunuz.

  • Tekrar tekrar tohumlanacak şekilde tasarlanmamıştır. Rastgele özellikler yalnızca tek bir rastgele sayı dizisi için geçerlidir.
  • Integer.valueOf(Integer.toString(x)+Integer.toString(y))Çektiğiniz pikseller arasında büyük bir korelasyon var.

Ben sadece /programming/9624963/java-simplest-integer- esrar

public static int TileColor(int x, int y) {
    return hash(x ^ hash(y));
}

karma işlevinin nerede olabileceği


0

Sistemlerin geçerli saat zamanını böyle bir tohum olarak kullanmayı deneyebilirsiniz:

Random random = new Random(System.currentTimeMillis())

Umarım daha rastgele bir değer üretir.


Ancak bu, her defasında dame koordinatı için dame değeri yaratmaz.
yusufçuk

0

İşte benim geldiğim tek satırlık statik gölgelendirici işlevi - poltergeist (Noisy Ghost).

Bir 2D koordinat ve bir tohum alır ve istendiği gibi monoton hale getirir. Ekran çözünürlüğünden bağımsız olarak gerçek zamanlı fps'de çalışır. GPU'lar bunun içindir.

// poltergeist (noisy ghost) pseudo-random noise generator function
// dominic.cerisano@standard3d.com 03/24/2015

precision highp float;

float poltergeist(in vec2 coordinate, in float seed) 
{
    return fract(sin(dot(coordinate*seed, vec2(12.9898, 78.233)))*43758.5453); 
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) 
{   
    fragColor = vec4(poltergeist(fragCoord, iGlobalTime)); 
}

Herhangi bir çözünürlük, herhangi bir doku, herhangi bir cihazda (mobil de) GL'yi destekler (ekranla hemen hemen her şeydir).

Şimdi burada çalıştığını görün!

https://www.shadertoy.com/view/ltB3zD

Bu gölgelendiriciyi standart opengl kullanarak java programınıza veya standart webgl kullanan herhangi bir tarayıcıya kolayca ekleyebilirsiniz.

Sadece eğlence için, herkesin Poltergeist'i tüm cihazlarda kalite ve performansta yenmesi için eldiveni atıyorum. Gürültülü Hayalet kuralları! Yenilmez!

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.