Obecnie pracuję nad programem, który powinien generować losowy szum na ekranie na podstawie „współrzędnych” piksela. Współrzędne powinny mieć ten sam kolor przy każdym ponownym uruchomieniu programu. Jednak korzystając z Java.Random, wyniki, które otrzymuję, nie są tak losowe, jak bym chciał:
Pomyślałem, że jeśli użyję połączonych współrzędnych (jak w jednej liczbie całkowitej utworzonej z obu współrzędnych obok siebie), każda ze współrzędnych będzie miała inną liczbę. Korzystając z tej liczby jako zarodka, spodziewałem się uzyskać różne liczby losowe dla każdej współrzędnej do użycia dla wartości rgb tej współrzędnej.
Oto kod, którego użyłem:
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;
}
}
Czy wzorzec, który program tworzy, wynika ze sposobu, w jaki działa funkcja Random w Javie, czy robię coś źle i czy powinienem spróbować zastosować inne podejście?
Aktualizacja: próbowałem teraz pozbyć się problemów związanych z konkatenacją, używając następującego kodu:
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;
}
W jakiś sposób zapewniło to (moim zdaniem) wystarczająco losowy obraz:
Ten kod jest jednak resetowany trzy razy na piksel. Mimo że obecnie nie jest to dla mnie problemem, rozważam zmianę tego kodu na wypadek, gdyby później potrzebowałem lepszej wydajności.
Odpowiedzi:
java.util.Random
Klasa Javy zwykle daje ci ciąg liczb pseudolosowych, które są wystarczająco dobre do użycia w grach 1 . Jednak ta cecha dotyczy tylko sekwencji wielu próbek opartych na nasionach. Gdy ponownie zainicjujesz RNG z rosnącymi wartościami początkowymi i spojrzysz tylko na pierwszą wartość każdej sekwencji, charakterystyka losowości nie będzie prawie tak dobra.Co możesz zamiast tego zrobić:
Zamiast używać generatora liczb losowych, użyj funkcji podsumowania wiadomości, aby zamienić parę współrzędnych na wartość koloru. Dane wyjściowe większości MDF są wystarczająco nieprzewidywalne, aby spełnić większość testów losowości. Dane wyjściowe są zwykle większe niż 24-bitowe wartości RGB, ale ich obcięcie zwykle nie stanowi problemu.
Aby poprawić wydajność, możesz połączyć generowanie podsumowania wiadomości z fragmentami. Generuj małe fragmenty pikseli, które są wystarczająco duże, aby wykorzystać pełną długość jednego wyjścia funkcji skrótu.
1, gdy absolutnie niezbędne jest, aby nikt nie mógł przewidzieć następnej liczby, użyj wolniejszej, ale mniej przewidywalnej
java.security.SecureRandom
źródło
W takim przypadku należy użyć deterministycznej funkcji szumu, takiej jak szum Perlina lub hałas jednostronny .
( Zobacz to pytanie, aby uzyskać więcej informacji na temat hałasu Perlina z kilkoma ładnymi zdjęciami. )
W większości przypadków użycie wbudowanej
random()
lub podobnej funkcji daje różne wartości przy każdym uruchomieniu programu, ponieważ mogą one wykorzystywać zegar jako dane wejściowe lub inne wartości pseudolosowe.Inną opcją jest wygenerowanie „mapy hałasu” raz, offline, a następnie wykorzystanie jej jako źródła liczb losowych w późniejszym terminie.
W swojej implementacji łączysz reprezentacje ciągów x i
y
. To źle, ponieważ nie jest unikalne w całej domenie. Na przykład,Powodzenia!
źródło
Zobaczmy, co dokładnie robisz:
Wszystko to brzmi dobrze, ale otrzymujesz wzór, ponieważ:
Piksel przy 1,11 i piksel przy 11,1 są zaszczepione liczbą 111, więc na pewno będą miały ten sam kolor.
Ponadto, o ile zawsze wykonujesz cykl w ten sam sposób, możesz używać tylko jednego generatora, nie musisz używać jednego dla każdego piksela. Wystarczy jeden na cały obraz! Nadal będą jakieś wzorce z powodu pseudolosowości. @David_Lively ma rację, używając algorytmu Noise, dzięki czemu będzie wyglądał bardziej losowo.
źródło
Stwórz generator kolorów, a następnie utwórz kolory dla swojej płytki. Nasiona tylko raz! Nie musisz wysiewać więcej, przynajmniej na płytkę.
A użycie będzie następujące:
Dzięki temu, jeśli nie jesteś zadowolony z wyniku, po prostu zmień
Random
ziarno. Musisz tylko przechowywać / komunikować ziarno i rozmiary, aby wszyscy klienci mieli ten sam obraz.źródło
Zamiast używać Random, rozważ użycie skrótu skrótu, takiego jak MD5. Zapewnia trudną do przewidzenia „losową” wartość na podstawie określonego wejścia, ale zawsze taką samą wartość dla tego samego wejścia.
Przykład:
UWAGA: Nie wiem, skąd pochodzi Color.rgb888 (..), więc nie wiem, jaki jest dozwolony zakres. 0-255 jest jednak normalne.
Ulepszenia do rozważenia:
źródło
Inni zauważyli, że jednym ze sposobów uzyskania pożądanego zachowania jest użycie funkcji skrótu, czyli „funkcji podsumowania wiadomości”. Problem polega na tym, że często są one oparte na algorytmach takich jak MD5, który jest kryptograficznie bezpieczny (tj. Naprawdę, naprawdę, naprawdę losowy), ale bardzo powolny. Jeśli używasz kryptograficznej funkcji skrótu za każdym razem, gdy potrzebujesz losowego piksela, napotkasz dość poważne problemy z wydajnością.
Istnieją jednak niekryptograficzne funkcje skrótu, które mogą generować wartości, które są wystarczająco losowe dla twojego celu, a jednocześnie szybkie. Zwykle sięgam po szmer . Nie jestem użytkownikiem Java, ale wygląda na to, że dostępna jest co najmniej jedna implementacja Java . Jeśli okaże się, że naprawdę musisz wygenerować każdy piksel ze współrzędnych, zamiast generować je wszystkie jednocześnie i przechowywać je w teksturze, byłby to dobry sposób na zrobienie tego.
źródło
Użyłbym liczby pierwszej ponad 2000 (maksymalna typowa rozdzielczość).
To zminimalizuje (lub wyeliminuje zduplikowane nasiona)
źródło
Random
jest wystarczająco losowy. Używasz go źle z dwóch głównych powodów.Integer.valueOf(Integer.toString(x)+Integer.toString(y))
między pikselami, które wysiewasz.Chciałbym po prostu użyć pewnej odmiany następującego kodu, w którym możesz wybrać funkcję skrótu (nie używaj Integer.getHashCode) z odpowiedzi na /programming/9624963/java-simplest-integer- haszysz
gdzie może być funkcja skrótu
źródło
Możesz spróbować użyć aktualnego czasu systemowego jako źródła:
Mam nadzieję, że przyniesie bardziej losową wartość.
źródło
Oto wymyślona przeze mnie liniowa funkcja cieniowania statycznego - poltergeist (Noisy Ghost).
Pobiera współrzędną 2D i ziarno i renderuje monotonicznie zgodnie z żądaniem. Działa w czasie rzeczywistym fps, niezależnie od rozdzielczości ekranu. Po to są GPU.
Dowolna rozdzielczość, dowolna tekstura, na dowolnym urządzeniu (również mobilnym) obsługującym GL (który jest praktycznie dowolny z ekranem).
Zobacz, jak działa tutaj, właśnie teraz!
https://www.shadertoy.com/view/ltB3zD
Możesz łatwo włączyć ten moduł cieniujący do programu Java za pomocą standardowego OpenGL lub w dowolnej przeglądarce za pomocą standardowego webgl.
Dla zabawy rzucam rękawicę, aby każdy mógł pokonać Poltergeist pod względem jakości i wydajności na wszystkich urządzeniach. Noisy Ghost rządzi! Niepokonany!
źródło