Interaktive Mandelbrot-Set-Bilder

Oct 02 2020

Der Zweck dieses Projekts ist es, ein interaktives Mandelbrot-Set zu generieren . Der Benutzer kann den Vergrößerungsgrad über die Befehlszeile festlegen und auf das erzeugte Bild klicken, um das Bild an dieser Stelle zu vergrößern.

Hier ist eine Datentypimplementierung für komplexe Zahlen:

public class Complex
{
    private final double re;
    private final double im;

    public Complex(double re, double im)
    { 
        this.re = re; 
        this.im = im; 
    }
    public double re() 
    { 
        return re; 
    }
    public double im()
    { 
        return im; 
    }
    public double abs()
    { 
        return Math.sqrt(re*re + im*im); 
    }
    public Complex plus(Complex b)
    {
        double real = re + b.re;
        double imag = im + b.im;
        return new Complex(real, imag);
    }
    public Complex times(Complex b)
    {
        double real = re*b.re - im*b.im;
        double imag = re*b.im + im*b.re;
        return new Complex(real, imag);
    }
    public Complex divide(Complex b)
    {
        double real = (re*b.re + im*b.im) / (b.re*b.re + b.im*b.im);
        double imag = (im*b.re - re*b.im) / (b.re*b.re + b.im*b.im);
        return new Complex(real, imag);
    }
    public boolean equals(Complex b)
    {
        if (re == b.re && im == b.im) return true;
        else                          return false;
    }
    public Complex conjugate() 
    {
        return new Complex(re, -1.0*im);
    }
    public String toString()
    {
        return re + " + " + im + "i";
    }
}

Hier ist mein Programm:

import java.awt.Color;

public class InteractiveMandelbrot {
    private static int checkDegreeOfDivergence(Complex c) {
        Complex nextRecurrence = c;
        
        for (int i = 0; i < 255; i++) {
            if (nextRecurrence.abs() >= 2) return i;
            nextRecurrence = nextRecurrence.times(nextRecurrence).plus(c);
        }
        return 255;
    }
    private static Color[] createRandomColors() {
        Color[] colors = new Color[256];
        double r = Math.random();
        int red = 0, green = 0, blue = 0;
        
        for (int i = 0; i < 256; i++) {
            red = 13*(256-i) % 256;
            green = 7*(256-i) % 256;
            blue = 11*(256-i) % 256;
            colors[i] = new Color(red,green,blue);
        }
        return colors;
    }
    private static void drawMandelbrot(double x, double y, double zoom) {
        StdDraw.enableDoubleBuffering();
        
        Color[] colors = createRandomColors();
        
        int resolution = 1000;
        int low = -resolution / 2;
        int high = resolution / 2;
        double xLowScale = x + zoom*(1.0 * low / resolution);
        double xHighScale = x + zoom*(1.0 * high / resolution);
        double yLowScale = y + zoom*(1.0 * low / resolution);
        double yHighScale = y + zoom*(1.0 * high / resolution);
        StdDraw.setXscale(xLowScale, xHighScale);
        StdDraw.setYscale(yLowScale, yHighScale);

        for (int i = low; i < high; i++) {
            for (int j = low; j < high; j++) {
                double realPart = zoom*(1.0 * i / resolution) + x;
                double imaginaryPart = zoom*(1.0 * j / resolution) + y;
                Complex c = new Complex(realPart,imaginaryPart);
                int degreeOfDivergence = checkDegreeOfDivergence(c);
                Color color = colors[degreeOfDivergence];

                StdDraw.setPenColor(color);
                double radius = 1.0 / (resolution * 2 / zoom);
                StdDraw.filledSquare(realPart, imaginaryPart, radius);
            }
        }
        StdDraw.show();
    }
    public static void main(String[] args) {
        double x = Double.parseDouble(args[0]);
        double y = Double.parseDouble(args[1]);
        int magnifier = Integer.parseInt(args[2]);
        double zoom = 1;

        drawMandelbrot(x, y, zoom);

        while (true) {
            if (StdDraw.isMousePressed()) {
                x = StdDraw.mouseX();
                y = StdDraw.mouseY();
                zoom = zoom/magnifier;

                drawMandelbrot(x, y, zoom);
            }
        }
    }
}

StdDraw ist eine einfache API, die von den Autoren des Buches Computer Science An Interdisciplinary Approach geschrieben wurde . Ich habe mein Programm überprüft und es funktioniert. Hier ist eine Instanz davon.

Eingabe: 0,5 0,5 10

Ausgabe (eigentlich eine Folge von Ausgaben):

Ich habe Farbe verwendet, um zu zeigen, wo ich geklickt habe.

Aus meinen eigenen vorherigen Beiträgen weiß ich bereits, wie ich mich verbessern kann Complex. In diesem Beitrag bin ich ausschließlich an der Verbesserung von interessiert InteractiveMandelbrot. Gibt es eine Möglichkeit, mein Programm zu verbessern?

Danke für Ihre Aufmerksamkeit.

Antworten

3 harold Oct 03 2020 at 23:30

Leistung: die komplexe Klasse

Sie haben erwähnt, dass Sie bereits wissen, wie Sie es verbessern können, aber die größte Leistungsverbesserung dieses Programms besteht darin , die Complex-Klasse zu entfernen . Vielleicht ist es traurig, sich von dieser Klasse zu verabschieden, aber einige Leistungstests durchzuführen und zu entscheiden.

Auf meinem PC 0.5, 0.5, 10läuft der ursprüngliche Code am Punkt / Zoom in 100 - 110 ms pro Bild. Mit Ausnahme der Zeichenzeit habe ich diese entfernt und die Farben einfach in einem Array gespeichert. Ausgenommen der erste Lauf, der langsamer ist als bei Java üblich.

Wenn ich die komplexe Arithmetik inline schreibe, ohne sie grundlegend zu ändern, dann:

private static int checkDegreeOfDivergence(double r, double i) {
    double a = r, b = i;
    
    for (int j = 0; j < 255; j++) {
        if (Math.sqrt(a * a + b * b) >= 2) return j;
        
        double nextA = a * a - b * b + r;
        double nextB = a * b + b * a + i;
        a = nextA;
        b = nextB;
    }
    return 255;
}

Jetzt läuft es in 30 - 40ms pro Frame. Etwa dreimal so schnell, nur durch einfaches Refactoring.

Die Logik kann leicht optimiert werden, aber die Verbesserung davon ist viel weniger signifikant.

private static int checkDegreeOfDivergence(double r, double i) {
    double a = r, b = i;
    
    for (int j = 0; j < 255; j++) {
        if ((a * a + b * b) >= 4) return j;
        
        double nextA = a * a - b * b + r;
        double nextB = a * b * 2 + i;
        a = nextA;
        b = nextB;
    }
    return 255;
}
3 user985366 Oct 03 2020 at 03:47

Kommentar 1

Sie verwenden die rVariable in nicht createRandomColors, daher ist nichts Zufälliges darin.

Kommentar 2

if (nextRecurrence.abs() >= 2) return i;

Sie sollten nachher Klammern verwenden. ifVersuchen Sie nicht, klug zu sein, indem Sie Einzeiler wie diesen schreiben. Es wird dich eines Tages beißen, wenn du es falsch liest oder bearbeitest, ohne es zu merken.

Kommentar 3

for (int i = low; i < high; i++) {
    for (int j = low; j < high; j++) {
        double realPart = zoom*(1.0 * i / resolution)

Die Berechnung zoom * 1.0 / resolutionwird dreimal in dieser Doppelschleife berechnet for, das ist für jedes Pixel auf dem Bildschirm, denke ich. Es kann sich lohnen, Ihre Berechnungen zu optimieren, indem Sie diesen Faktor herausnehmen und nur einmal berechnen.

Da es nicht von ioder abhängt j, können Sie diese Berechnung vollständig aus der Schleife entfernen und sie daher einmal pro Bildschirm anstatt 3 * Millionen Mal berechnen.

Ebenso realParthängt das nicht davon ab j, also berechnen Sie es außerhalb der jSchleife, um sich 999/1000 (ungefähr) Berechnungen für zu sparen realPart.

double realPart = zoom*(1.0 * i / resolution) + x;