Mandelbrot set displays incorrectly

给你一囗甜甜゛ 提交于 2019-12-07 02:00:51

问题


This is my attempt to program the Mandelbrot set in Python 3.5 using the Pygame module.

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()

I believe that the iteration algorithm is correct, but the output doesn't look right:

Wondering what might be the problem? Sorry for the code dump, just not sure which part may cause it to look like that.


回答1:


Fascinating bug -- it literally looks like a squashed bug :)

The problem lies in the two lines:

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

You are changing the meaning of a in the first line, hence using the wrong a in the second.

The fix is as simple as:

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

which will cause the same value of a to be used in calculating the right hand side.

It would be interesting to work out just what your bug has produced. Even though it isn't the Mandelbrot set, it seems to be an interesting fractal in its own right. In that sense, you had a very lucky bug. 99% percent of the times, bugs lead to garbage, but every now and then they produce something quite interesting, but simply unintended.

On Edit:

The Mandelbrot set is based on iterating the complex polynomial:

f(z) = z^2 + c

The pseudo-Mandelbrot set which this bug has produced is based on iterating the function

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

where Re() and Im() are the operators which extract the real and imaginary parts of a complex number. This isn't a polynomial in z, though it is easy to see that it is a polynomial in z,z* (where z* is the complex conjugate of z). Since it is a fairly natural bug, it is almost certain that this has appeared somewhere in the literature on the Mandelbrot set, though I don't remember ever seeing it.




回答2:


I decided to learn about the mandelbrot set and wrote my own version! I used python's complex data type, which should make the mandelbrot calculation for each pixel a bit clearer. Here is a screenshot of the result:

And here is the source code/code dump:

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)

The two issues with this code are that one, it is quite slow in updating the mandelbrot set, and two, the aspect ratios get distorted if you work with non-square windows or box-selections. Let me know if any of the code is unclear!



来源:https://stackoverflow.com/questions/42047993/mandelbrot-set-displays-incorrectly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!