Mandelbrot - Optimierung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Ich kenn mich wiederum nicht mit Mandelbrot-Mengen aus. Aber wie wärs wenn du den array an diesen Stellen einfach mit NaN (numpy.NAN) belegst? Das ganze lässt sich dann sogar mit NAN noch plotten und du weisst außerdem genau an welcher Stelle es nicht konvergierte.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Ich kenne mich mit numpy-arrays und matplotlib nicht aus ...
Dimensioniere dein array doch so, dass die Länge auch bei "ungünstigen" Startwerten ausreicht. Um die jeweils nächste Zelle zu befüllen, wirst du ja einen Zeiger mitführen, der am Ende auch die Gesamtlänge der tatsächlich zu plottenden Punkte angibt. Dann erzeugst du daraus per slicing ein entsprechend verkürztes array und übergibst dies zum Plotten.
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Ok, ich verzweifle inzwischen fast den ganzen Nachmittag / Abend, da ich das mit den Arrays überhaupt nicht gebacken kriege. Das Script hört auf zu laufen; ohne Fehlermeldung oder so. Sehr wahrscheinlich liegt's an mir. ^^ Wäre um jede Art von Hilfe dankbar.

Code: Alles auswählen

#!/usr/bin/python
# Filename: mandelbrot.py

from numpy import *
import matplotlib.pyplot as plt
import pylab

def mandelbrot(start, steps, stepx, stepy, iterations): 

    c = start
    c0 = start
    
    a = zeros((1,2))

    for h in xrange(0,steps):
        for k in xrange(0,steps): # do this for all points in the selected area
        
            if abs(c) >= 2: # check divergence
                start += stepx
            
            else:
            
                for i in xrange(0,iterations):
                    
                    c = c * c + c0

                    if abs(c) >= 2: # check divergence of value
                        break
                
                else:
                    b = array([round(c0.real,2),round(c0.imag,2)])
                    vstack((a,b))
                
                start += stepx
         
            c = start
            c0 = start
    
        start = start + stepy - complex(steps * stepx.real,0)

    pylab.imshow(a)
    pylab.show

mandelbrot(complex(-3,-3), 601, complex(0.01,0), complex(0,0.01), 100)
MfG
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

So geths schon mal wesentlich schneller als am Anfang:

Code: Alles auswählen

import numpy
import matplotlib.pyplot as plt

def mandelbrot(steps, start, stepx, stepy, iterations):
    c = start
    c0 = start

    res = []

    for h in xrange(steps):
        for k in xrange(steps):  # do this for all points in the selected area
            if abs(c) >= 2:      # check divergence
                start += stepx
            else:
                for i in xrange(iterations):
                    if abs(c * c + c0) >= 2:  # check divergence of value
                        break
                    else:
                        c = c * c + c0
                else:
                    res.append(c0)
                start += stepx
            c = start
            c0 = start
        start = start + stepy - complex(steps * stepx.real,0)

    return res

if __name__ == "__main__":
    res = mandelbrot(601, complex(-3,-3), complex(0.01,0), complex(0,0.01), 100)
    res = numpy.array(res)
    plt.plot(res.real, res.imag, "k,")

    plt.xlabel(r'$re(z)$')
    plt.ylabel(r'$i \cdot im(z)$')
    plt.title('The Mandelbrot Set')

    plt.axis([-3,2,-2,2])
    plt.grid(True)
    plt.show()
Die Daten werden zwar jetzt erst in einer Python-Liste gespeichert, aber das ist, wenn vorher die Größe nicht bekannt ist vielleicht sogar die bessere Wahl.
Der wesentliche unterschied ist das du nicht für jeden Punkt ein matplotlib.lines.Line2D-Object erstellst sondern nur eine relativ kompaktes Python-complex-Objekt. Die Daten werden dann als Ganzes zum Plotten ühbergeben. Das geht wesentlich schneller als einzeln. Das ist wie wenn du in einer GUI-Library statt ein Bild zu laden jeden Punkt einzeln mit einer kurzen Linie zeichnest.
Das es noch wesentlich schneller geht zeigt das Beispiel was oben schon mal erwähnt wurde.

MFG HerrHagen
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Ok, danke vielmals. Wusste gar nicht, dass man bei Python komplexe Zahlen in Listen speichern kann. Gehe ich richtig davon aus, dass das if __name__ == "__main__": nicht essentiell ist, sondern nur existiert, damit die Funktion mandelbrot in anderen Files wiederverwendbar ist und nur bei Aufruf von mandelbrot.py geplottet wird?

Gibt es ansonsten nichts, was noch suboptimal ist und die Geschwindigkeit beeinflusst? Inzwischen braucht das Programm etwa 1-4 Sekunden; als ich noch jeden Punkt einzeln plottete, bedurfte es gerne mal 15-20 Sekunden. Und wie kann ich das vorgefertigte Interface von matplotlib verändern? Also einzelne Aktionen verändern oder rausnehmen?

MfG

//e: Noch eine Verständnisfrage: Warum muss ich die Liste in ein numpy.array transformieren? Habe die Zeile res = numpy.array(res) mal auskommentiert und bemerkt, dass es dann nichts mit den Angaben real und imag anfangen kann; d.h. eine Fehlermeldung ausgibt. Warum ist das so? Warum funktioniert das Script mit dem numpy.array, nicht aber mit den Python-Listen?
BlackJack

@Huy: Die Funktion der ``if __name__``-Abfrage hast Du richtig erkannt, nur ihre Wichtigkeit für sauberen Code noch nicht. Also ich finde sie "essentiell", weil man das Modul dadurch ohne Nebeneffekte importieren kann. Um es zu testen, um Dokumentation generieren zu lassen und so weiter.

Zum Edit: Listen haben halt keine Attribute die `real` und `imag` heissen.
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Aber ich speichere ja komplexe Zahlen, dh. in der Liste müsste c0 als complex(x,y) gespeichert sein, oder etwa nicht? BTW versuche ich gerade, das ganze farblich etwas ansprechender zu gestalten, also wie hier:

http://www.scipy.org/Tentative_NumPy_Tu ... et_Example

Nur habe ich keine Ahnung, wie ich das machen soll. plot() verlangt ja eine Eingabe in Form von plot(x1,y1,color1,x2,y2,color2...), wenn ich nun aber die einzelnen Farben in einer Liste colour speichere (da die RGB Werte von i abhängig sein sollen) und anschliessend schreibe plt.plot(res.real, res.imag, c=colour), kommen dutzende von Fehlermeldungen. Ausserdem - gibt es eine Möglichkeit, spät-divergierende Punkte (die zB i = 80 erreichen) in einer Liste zu speichern, ohne globale Variablen zu benutzen? Habe das nämlich mal mit globalen Variablen versucht zu machen, nur soll ja dann die Rechnerei länger dauern oder so.

MfG
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Huy hat geschrieben:Aber ich speichere ja komplexe Zahlen, dh. in der Liste müsste c0 als complex(x,y) gespeichert sein, oder etwa nicht?
Ja, und? Es ist immer noch eine Liste, die ändert ihre Attribute ja nicht einfach.
Huy hat geschrieben:Nur habe ich keine Ahnung, wie ich das machen soll. plot() verlangt ja eine Eingabe in Form von plot(x1,y1,color1,x2,y2,color2...), wenn ich nun aber die einzelnen Farben in einer Liste colour speichere (da die RGB Werte von i abhängig sein sollen) und anschliessend schreibe plt.plot(res.real, res.imag, c=colour), kommen dutzende von Fehlermeldungen.
Dann baue eben eine neue Liste auf, welche diese Form hat.
Huy hat geschrieben:Ausserdem - gibt es eine Möglichkeit, spät-divergierende Punkte (die zB i = 80 erreichen) in einer Liste zu speichern, ohne globale Variablen zu benutzen? Habe das nämlich mal mit globalen Variablen versucht zu machen, nur soll ja dann die Rechnerei länger dauern oder so.
Indem du eine Liste im lokalen Namensraum erstellst.
Das Leben ist wie ein Tennisball.
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Indem du eine Liste im lokalen Namensraum erstellst.
Und wie soll ich ausserhalb der Funktion dann auf diese Liste zugreifen?

MfG
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Huy hat geschrieben:Und wie soll ich ausserhalb der Funktion dann auf diese Liste zugreifen?
Rückgabewerte
Das Leben ist wie ein Tennisball.
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Ehm, aber ich gebe doch bei mandelbrot() schon res aus. Da käme doch alles ziemlich durcheinander, wenn ich zusätzlich noch die Farben ausgebe, oder etwa nicht?

MfG
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Huy hat geschrieben:Ehm, aber ich gebe doch bei mandelbrot() schon res aus. Da käme doch alles ziemlich durcheinander, wenn ich zusätzlich noch die Farben ausgebe, oder etwa nicht?
Tupel ;-)
Das Leben ist wie ein Tennisball.
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Sind Tupel nicht unveränderbar? Oo

MfG
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Huy hat geschrieben:Sind Tupel nicht unveränderbar? Oo

MfG
Hast du das Tutorial eigentlich jemals gelesen?

Code: Alles auswählen

def spam():
    res = [1, 2, 3]
    eggs = 42

    return res, eggs

a, b = spam()
print a
print b
Das Leben ist wie ein Tennisball.
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Wo ist in deinem Code bitteschön ein Tupel?

MfG
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Huy hat geschrieben:Wo ist in deinem Code bitteschön ein Tupel?
Und das steht im Tutorial ;-)
Das Leben ist wie ein Tennisball.
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Ich habe zwar nach wie vor keine Ahnung, was ich mit Tupel soll, wo in deinem Code ein Tupel ist und von welchem allerleuchtenden Tutorial du sprichst, aber ich probier' halt dann mal selber weiter...

MfG
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Huy hat geschrieben:Ich habe zwar nach wie vor keine Ahnung, was ich mit Tupel soll,
Mehrere Werte gleichzeitig zurückgeben...
Huy hat geschrieben:wo in deinem Code ein Tupel ist
... Zeile 5...
Huy hat geschrieben:und von welchem allerleuchtenden Tutorial du sprichst
... das aus den offiziellen Python-Docs.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Huy
User
Beiträge: 18
Registriert: Freitag 23. April 2010, 20:25

Ich dachte, es sei von einem Tupel in diesem Sinne die Rede:
http://de.wikibooks.org/wiki/Python-Pro ... ngen#Tupel

:roll: Wie dem auch sei, ich les' dann halt noch etwas mehr und versuche es selber noch ein bisschen. Danke für die Aufklärung.

MfG

//e: Jaaaa, ich bin dumm, kthx.
Zuletzt geändert von Huy am Montag 26. April 2010, 16:45, insgesamt 1-mal geändert.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

lol? Da stehts sogar gleich am Anfang

Code: Alles auswählen

>>> 1, 2, 3
(1, 2, 3)
Um die Lesbarkeit zu erhöhen, sollten Tuple immer mit runden Klammern erzeugt werden. Dabei ist ein Spezialfall zu beachten: Um ein Tupel mit nur einem Eintrag zu erhalten, wird ein abschließendes Komma verwendet.
the more they change the more they stay the same
Antworten