Wpisany przez Tomasz Lubiński,
08 listopada 2011 11:21
Buddhabrot jest fraktalem zbudowanym w oparciu o zbiór Mandelbrot'a. Jego nazwa pochodzi od podobieństwa do obrazu Buddy, siedzącego w pozie medytacyjnej. Technikę uzyskania tego obrazu odkryła i opisała w roku 1993 Melinda Green.
Najpierw przypomnijmy sobie definicję zbióru Mandelbrota. Jest to zbiór liczb zespolonych p takich, że ciąg liczb zespolonych z0, z1, z2, ... o wartościach zdefiniowanych następująco:
z0 = 0
z1 = z0 + p
z2 = z1 + p
...
zn+1 = zn + p
nie dąży do nieskończoności
W praktyce sprawdza się czy punkt p należy do zbioru mandelbrota z pewnym przybliżeniem:
By utworzyć fraktal Buddhabrot najpierw zdefiniujemy:
Proces tworzenia fraktala wygląda następująco:
Należy zauważyć, że tablica liczników przechowuje wartości liczników tylko dla określonych wartości. Na przykład jeżeli ma ona rozmiar 30 na 30, to kolejne jej punkty odpowiadają wartościom: (-1.5; -2i), (-1.4, -2i), (-1.3, -2i), ... (-1.5; -1.9i), (-1.4; -1.9i), (-1.3; -1.9i), ... Co zrobić gdy punkt uzyskany w trakcie obliczania przybliżenia ma wartość na przykład (-1.32, -1.99i)? Jak do tego wszystkiego się zabrać? Najpierw obliczmy jakie odległości są pomiędzy kolejnymi licznikami. Przy czym osobno należy obliczyć odległości w poziomie (oznaczmy ją jako odl_x) i odległości w pionie (oznaczmy ją jako odl_y)
odl_x obliczymy następująco: (maksymalna wartość X - minimalna wartość X) / wielkość tablicy liczników w poziomie
odl_y obliczymy następująco: (maksymalna wartość Y - minimalna wartość Y) / wielkość tablicy liczników w pionie
Dla naszego przykładu 30 na 30 będzie to: odl_x = 1.5 - (-1.5) / 30 = 3 / 30 = 0.1, odl_y = 1.0 - (-2.0) / 30 = 3 / 30 = 0.1. Zatem różnica pomiędzy sąsiednimi punktami w pionie i poziomie będzie wynosić 0.1 (zgadza się to z wartościami, które przytoczyłem wcześniej).
Jak zatem obliczyć, który licznik zwiększyć dla obliczonego punktu (x, y). Można to zrobić na kilka sposobów:
Ja w swoim programie zastosowałem ostatnie podejście. Cechą charakterystyczną dla tego fraktala jest to, że im zastosujemy większą wartość maksymalnego przybliżenia tym więcej szczegółów zobaczymy. Widać to znakomicie na przykładach poniżej:
Najpierw przypomnijmy sobie definicję zbióru Mandelbrota. Jest to zbiór liczb zespolonych p takich, że ciąg liczb zespolonych z0, z1, z2, ... o wartościach zdefiniowanych następująco:
z0 = 0
z1 = z0 + p
z2 = z1 + p
...
zn+1 = zn + p
nie dąży do nieskończoności
W praktyce sprawdza się czy punkt p należy do zbioru mandelbrota z pewnym przybliżeniem:
- 1 przybliżenie: wszystkie punkty
- 2 przybliżenie: |z1| < 2
- 3 przybliżenie: |z1| < 2 oraz |z2| < 2
- 4 przybliżenie: |z1| < 2 oraz |z2| < 2 oraz |z3| < 2
- ...
- n-te przybliżenie: |z1| < 2 oraz |z2| < 2, ... |zn-1| < 2
By utworzyć fraktal Buddhabrot najpierw zdefiniujemy:
- dwuwymiarową tablicę liczników (liczba elementów tablicy będzie odpowiadała rozmiarowi generowanego obrazu). Niech pierwszy licznik w pierwszym rzędzie odpowiada współrzędnej -1.5 - 2i, natomiast ostatni licznik w ostatnim rzędzie odpowiada współrzędnej 1.5 + 1.0i (dzięki takim współrzędnym otrzymamy obraz całego fraktala, jeżeli chcemy uzyskać jedynie jakiś fragment, należy te wartości zawężyć),
- maksymalne przybliżanie (ang. baiulout),
- oraz liczbę punktów, dla których będziemy generować fraktal
Proces tworzenia fraktala wygląda następująco:
- ustawiamy wartości wszystkich liczników w tablicy na 0
- dla zadanej liczby punktów wykonujemy następujące czynności:
- losujemy wartość p z zakresu -2.0 - 2.0i do 2.0 + 2.0i
- obliczamy z jakim przybliżeniem punkt p należy do zbioru Mandelbrot'a (obliczenia wykonujemy do maksymalnego zadanego przybliżenia ang. baiulout)
- jeżeli punkt p należy do zbioru Mandelbrot'a z maksymalnym zadanym przybliżeniem, to losujemy kolejny punkt p
- jeżeli punkt p należy do zbioru Mandelbrot'a z przybliżeniem mniejszym niż zadane maksymalne to bierzemy ciąg liczb z0, z1, z2, ... uzyskanych w trakcie obliczania przybliżenia i dla każdej liczby z ciągu zwiększamy odpowiedni licznik w tablicy liczników
- tablicę liczników przedstawiamy w formie graficznej - duże wartości zaznaczamy kolorem jasnym małe kolorem ciemnym, przy czym warto tutaj zastosować skalę logarytmiczną, w ten sposób uzyskujemy fraktal Buddhabrot
Należy zauważyć, że tablica liczników przechowuje wartości liczników tylko dla określonych wartości. Na przykład jeżeli ma ona rozmiar 30 na 30, to kolejne jej punkty odpowiadają wartościom: (-1.5; -2i), (-1.4, -2i), (-1.3, -2i), ... (-1.5; -1.9i), (-1.4; -1.9i), (-1.3; -1.9i), ... Co zrobić gdy punkt uzyskany w trakcie obliczania przybliżenia ma wartość na przykład (-1.32, -1.99i)? Jak do tego wszystkiego się zabrać? Najpierw obliczmy jakie odległości są pomiędzy kolejnymi licznikami. Przy czym osobno należy obliczyć odległości w poziomie (oznaczmy ją jako odl_x) i odległości w pionie (oznaczmy ją jako odl_y)
odl_x obliczymy następująco: (maksymalna wartość X - minimalna wartość X) / wielkość tablicy liczników w poziomie
odl_y obliczymy następująco: (maksymalna wartość Y - minimalna wartość Y) / wielkość tablicy liczników w pionie
Dla naszego przykładu 30 na 30 będzie to: odl_x = 1.5 - (-1.5) / 30 = 3 / 30 = 0.1, odl_y = 1.0 - (-2.0) / 30 = 3 / 30 = 0.1. Zatem różnica pomiędzy sąsiednimi punktami w pionie i poziomie będzie wynosić 0.1 (zgadza się to z wartościami, które przytoczyłem wcześniej).
Jak zatem obliczyć, który licznik zwiększyć dla obliczonego punktu (x, y). Można to zrobić na kilka sposobów:
- indeks x w tablicy liczników = część całkowita((x - minimalna wartość x) / odl_x)
indeks y w tablicy liczników = część całkowita((y - minimalna wartość y) / odl_y),
czyli dla naszego przykładu mielibyśmy:
indeks x w tablicy liczników = część całkowita((-1.32 - (-1.5)) / 0.1) = część całkowita(1.8) = 1
indeks y w tablicy liczników = część całkowita((-1.99 - (-2.0)) / 0.1) = część całkowita(0.1) = 0
Należy zatem dla wartości (-1.32, -1.99i) zwiększyć o jeden licznik o indeksie (1, 0). - indeks x w tablicy liczników = zaokrągl do całkowitej((x - minimalna wartość x) / odl_x)
indeks y w tablicy liczników = zaokrągl do całkowitej((y - minimalna wartość y) / odl_y),
czyli dla naszego przykładu mielibyśmy:
indeks x w tablicy liczników = zaokrągl do całkowitej((-1.32 - (-1.5)) / 0.1) = zaokrągl do całkowitej(1.8) = 2
indeks y w tablicy liczników = zaokrągl do całkowitej((-1.99 - (-2.0)) / 0.1) = zaokrągl do całkowitej(0.1) = 0
Należy zatem dla wartości (-1.32, -1.99i) zwiększyć o jeden licznik o indeksie (2, 0). - Można też zastosować interpolacje dwuliniową:
indeks x w tablicy liczników = część całkowita((x - minimalna wartość x) / odl_x)
indeks y w tablicy liczników = część całkowita((y - minimalna wartość y) / odl_y)
błąd x = ((x - minimalna wartość x) / odl_x) - indeks x w tablicy liczników
błąd y = ((y - minimalna wartość y) / odl_y) - indeks x w tablicy liczników
Wówczas odpowiednie liczniki modyfikujemy następująco:
licznik (indeks x, indeks y) zwiększamy o (1 - błąd x)*(1 - błąd y)
licznik (indeks x + 1, indeks y) zwiększamy o (1 - błąd x)*(błąd y)
licznik (indeks x, indeks y + 1) zwiększamy o (błąd x)*(1 - błąd y)
licznik (indeks x + 1, indeks y + 1) zwiększamy o (błąd x)*(błąd y)
czyli dla naszego przykładu mielibyśmy:
indeks x w tablicy liczników = część całkowita((-1.32 - (-1.5)) / 0.1) = część całkowita(1.8) = 1
indeks y w tablicy liczników = część całkowita((-1.99 - (-2.0)) / 0.1) = część całkowita(0.1) = 0
błąd x = ((-1.32 - (-1.5)) / 0.1) - 1 = 0.8
błąd y = ((-1.99 - (-2.0)) / 0.1) - 0 = 0.1
Zatem licznik o indeksie (1, 0) zwiększamy o (1 - 0.8) * (1 - 0.1) = 0.2*0.9 = 0.18
Zatem licznik o indeksie (2, 0) zwiększamy o (1 - 0.8) * 0.1 = 0.2*0.1 = 0.02
Zatem licznik o indeksie (1, 1) zwiększamy o 0.8 * (1 - 0.1) = 0.8*0.9 = 0.72
Zatem licznik o indeksie (2, 1) zwiększamy o 0.8 * 0.1 = 0.08 = 0.08
Ja w swoim programie zastosowałem ostatnie podejście. Cechą charakterystyczną dla tego fraktala jest to, że im zastosujemy większą wartość maksymalnego przybliżenia tym więcej szczegółów zobaczymy. Widać to znakomicie na przykładach poniżej:
- maksymalne przybliżenie = 100
- maksymalne przybliżenie = 5000
Przykład w JavaScript:
Implementacje
Autor | Język programowania | Komentarz | Otwórz | Pobierz | Ocena |
Tomasz Lubiński | JavaScript | Firefox 3.0+, Safari 3.0+, Chrome 3.0+, Opera 9.5+, IE 9.0+ | .js | .js | ***** / 1 |
Poprawiony: 01 sierpnia 2012 19:35
Wydaje mi się, że najbardziej wrażliwymi elementami na tą zmienność jest generator licz pseudolosowych, oraz sposób wyliczania koloru na podstawie wartości minimalnej,maks ymalnej w tablicy licznik oraz licznik[x][y].
Nie przeprowadziłem jeszcze testów, ale wydaje się że:
1) załączenie bardziej losowego generatora niż systemowy
2) zmniejszenie wagi min i max, i zastosowanie średniej z wszystkich elementów licznik'a do kolorowania