2017-02-05 29 views
6

To jest moja próba zaprogramowania zestawu Mandelbrota w Pythonie 3.5 przy użyciu modułu Pygame.Zestaw Mandelbrota wyświetla niepoprawnie

import math, pygame 
pygame.init() 

def mapMandelbrot(c,r,dim,xRange,yRange): 
    x = (dim-c)/dim 
    y = (dim-r)/dim 
    #print([x,y]) 
    x = x*(xRange[1]-xRange[0]) 
    y = y*(yRange[1]-yRange[0]) 
    x = xRange[0] + x 
    y = yRange[0] + y 
    return [x,y] 

def checkDrawBox(surface): 
    for i in pygame.event.get(): 
     if i.type == pygame.QUIT: 
      pygame.quit() 
     elif i.type == pygame.MOUSEBUTTONDOWN: 
      startXY = pygame.mouse.get_pos() 
      boxExit = False 
      while boxExit == False: 
       for event in pygame.event.get(): 
        if event.type == pygame.MOUSEBUTTONUP: 
         boxExit = True 
       if boxExit == True: 
        return [startXY,pygame.mouse.get_pos()] 
       pygame.draw.rect(surface,[255,0,0],[startXY,[pygame.mouse.get_pos()[0]-startXY[0],pygame.mouse.get_pos()[1]-startXY[1]]],1) 
       pygame.display.update() 

def setup(): 
    dimensions = 500 
    white = [255,255,255] 
    black = [0,0,0] 
    checkIterations = 100 
    canvas = pygame.display.set_mode([dimensions,dimensions]) 
    canvas.fill(black) 
    xRange = [-2,2] 
    yRange = [-2,2] 
    xRangePrev = [0,0] 
    yRangePrev = [0,0] 
    newxRange = [0,0] 
    newyRange = [0,0] 
    while True: 
     if not ([xRange,yRange] == [xRangePrev,yRangePrev]): 
      draw(dimensions, canvas, xRange, yRange, checkIterations) 
      pygame.display.update() 
      xRangePrev = xRange 
      yRangePrev = yRange 
     box = checkDrawBox(canvas) 
     if box != None: 
      maxX = max([box[0][0],box[1][0]]) 
      maxY = max([box[0][1],box[1][1]]) 
      newxRange[0] = mapMandelbrot(box[0][0],0,dimensions,xRange,yRange)[0] 
      newxRange[1] = mapMandelbrot(box[1][0],0,dimensions,xRange,yRange)[0] 
      newyRange[0] = mapMandelbrot(0,box[0][1],dimensions,xRange,yRange)[1] 
      newyRange[1] = mapMandelbrot(0,box[1][1],dimensions,xRange,yRange)[1] 
      xRange = newxRange 
      yRange = newyRange 

def draw(dim, surface, xRange, yRange, checkIterations): 
    for column in range(dim): 
     for row in range(dim): 
      greyVal = iteration(0,0,mapMandelbrot(column,row,dim,xRange,yRange),checkIterations,checkIterations)  
      surface.set_at([dim-column,row],greyVal) 

def iteration(a, b, c, iterCount, maxIter): 
    a = (a*a) - (b*b) + c[0] 
    b = (2*a*b) + c[1] 
    iterCount = iterCount - 1 
    if iterCount == 0: 
     return [0,0,0] 
    elif abs(a+b) > 17: 
     b = (iterCount/maxIter)*255 
     return [b,b,b] 
    else: 
     return iteration(a,b,c,iterCount,maxIter) 


setup() 

wierzę, że algorytm iteracji jest poprawna, ale wyjście nie wygląda dobrze:

enter image description here

Zastanawiasz się, co może być problemem? Przepraszam za zrzut kodu, tylko nie wiem, która część może sprawiać, że tak wygląda.

+0

Na marginesie: użycie "abs (a + b)> 17" jest nietypowym efektem. Bardziej typowe jest 'a * a + b * b> = 2', a ponieważ będziesz potrzebował tych kwadratów w następnej iteracji, jest tanie. –

+0

Przepraszam, miałem na myśli '> = 4' –

Odpowiedz

9

Fascynujące bug - to dosłownie wygląda jak zgnieciony bug :)

Problem tkwi w dwóch wierszach:

a = (a*a) - (b*b) + c[0] 
b = (2*a*b) + c[1] 

Zmieniasz sens a w pierwszej linii, a więc za pomocą zła a w drugiej.

Rozwiązaniem jest tak proste, jak:

a, b = (a*a) - (b*b) + c[0], (2*a*b) + c[1] 

który spowoduje taką samą wartość a być stosowane przy obliczaniu prawej stronie.

Byłoby interesujące dowiedzieć się, co spowodował błąd. Chociaż nie jest to zestaw Mandelbrota, wydaje się być interesującym fraktalem. W tym sensie miałeś bardzo szczęśliwy bug. 99% procent czasu, błędy prowadzą do śmieci, ale co jakiś czas produkują coś bardzo interesującego, ale po prostu niezamierzone.

Na EDIT:

Zbiór Mandelbrota opiera się na iterację złożoną wielomian:

f(z) = z^2 + c 

Pseudo-Mandelbrot ustawić, które ten błąd jest produkowany w oparciu o iteracji funkcji

f(z) = Re(z^2 + c) + i*[2*Re(z^2 + c)*Im(z) + Im(c)] 

gdzie Re() i Im() są operatorami, którzy pobierają prawdziwe i urojone części liczby zespolonej. To nie jest wielomian w z, choć łatwo zauważyć, że jest to wielomian w z,z* (gdzie z* jest sprzężeniem zespolonym z z).Ponieważ jest to dość naturalny błąd, jest prawie pewne, że pojawiło się to gdzieś w literaturze na temat zbioru Mandelbrota, choć nie pamiętam, żeby kiedykolwiek to widziałem.

0

Postanowiłem zapoznać się z zestawem mandelbrota i napisałem własną wersję! Użyłem typu danych Pythona complex, co powinno nieco wyjaśnić obliczenia mandelbrota dla każdego piksela. Oto zrzut ekranu z wynikiem:

enter image description here

I tu jest zrzut kod źródłowy/code:

import pygame 
import sys 

def calc_complex_coord(x, y): 
    real = min_corner.real + x * (max_corner.real - min_corner.real)/(width - 1.0) 
    imag = min_corner.imag + y * (max_corner.imag - min_corner.imag)/(height - 1.0) 
    return complex(real, imag) 

def calc_mandelbrot(c): 
    z = c 
    for i in range(1, max_iterations+1): 
     if abs(z) > 2: 
      return i 
     z = z*z + c 
    return i 

def calc_color_score(i): 
    if i == max_iterations: 
     return black 
    frac = 255.0 - (255.0 * i/max_iterations) 
    return (frac, frac, frac) 

def update_mandelbrot(): 
    for y in range(height): 
     for x in range(width): 
      c = calc_complex_coord(x, y) 
      mandel_score = calc_mandelbrot(c) 
      color = calc_color_score(mandel_score) 
      mandel_surface.set_at((x, y), color) 

if __name__ == "__main__": 
    pygame.init() 
    (width, height) = (500, 500) 
    display = pygame.display.set_mode((width, height)) 
    pygame.display.set_caption("Mandelbrot Magic") 
    clock = pygame.time.Clock() 
    mandel_surface = pygame.Surface((width, height)) 
    black = (0, 0, 0) 
    red = (255, 0, 0) 
    max_iterations = 50 
    min_corner = complex(-2, -2) 
    max_corner = complex(2, 2) 
    box = pygame.Rect(0, 0, width, height) 
    update_mandel = True 
    draw_box = False 

    while True: 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       sys.exit() 
      elif event.type == pygame.MOUSEBUTTONDOWN: 
       x, y = event.pos 
       box = pygame.Rect(x, y, 0, 0) 
       draw_box = True 
      elif event.type == pygame.MOUSEMOTION: 
       x, y = event.pos 
       if draw_box: 
        box = pygame.Rect(box.left, box.top, x - box.left, y - box.top) 
      elif event.type == pygame.MOUSEBUTTONUP: 
       x, y = event.pos 
       update_mandel = True 

     display.blit(mandel_surface, (0, 0)) 
     if draw_box: 
      pygame.draw.rect(display, red, box, 1) 
     if update_mandel: 
      box.normalize() 
      new_min_corner = calc_complex_coord(box.left, box.top) 
      new_max_corner = calc_complex_coord(box.right, box.bottom) 
      min_corner, max_corner = new_min_corner, new_max_corner 
      update_mandelbrot() 
      update_mandel = False 
      draw_box = False 

     pygame.display.update() 
     clock.tick(60) 

Dwa problemy z tym kodem jest, że jeden, jest to dość powolny w aktualizacji zestaw mandelbrot, a dwa, współczynniki kształtu ulegają zniekształceniu, jeśli pracujesz z nieokreślonymi oknami lub wybranymi ramkami. Daj mi znać, jeśli jakiś kod jest niejasny!

+1

Wygląda na dobrego człowieka i dziękuję za uwzględnienie rysunku w pudełku, jednak szukałem konkretnego problemu w moim kodzie, ponieważ technicznie sądzę, że emulowałem złożony typ danych w sposób, który daje taką samą wartość dla każdego piksela, jak Ty. – lollingbirdz