SimulatedSort

Simulated Annealing ist eine Optimierungsmethode, die von natürlichen Kristallisationsprozessen inspiriert ist. Man startet in der Schmelze bei hohen Temperaturen und lässt es dann abkühlen, sodass die Atome sich in einem Zustand minimaler Energie anordnen, dem Kristallgitter. Wenn man also für ein Optimierungsproblem die zu optimierende Größe als Energie ansieht, und man eine Lösung durch eine kleine Änderung in eine andere Lösung verwandeln kann, kann man mit dieser Methode eine Näherung für das Optimum finden.

Wenn wir also eine Sequenz $S$ von $N$ Zahlen sortieren wollen, können wir die Summe der Differenzen zwischen benachbarten Zahlen als Energie betrachten, denn die ist minimal in einer sortierten Liste. \begin{equation} \mathcal{H} = \sum_{i=1}^{N-1} \left| S_i - S_{i+1} \right| \end{equation} Um eine Lösung in eine andere zu verwandeln, reicht es zwei Elemente der Sequenz zu tauschen.

Der Kern von Simulated Annealing ist der Metropolis Algorithmus.

  1. Starte bei einer hohen Temperatur $T$.
  2. Berechne die Energie $\mathcal{H}(S)$ der aktuellen Konfiguration $S$.
  3. Erzeuge eine neue Konfiguration $R$ durch eine kleine Änderunge von $S$.
  4. Akzeptiere $R$ mit der Wahscheinlichkeit $$p_\mathrm{acc} = \min\left[1 ,\exp(-(\mathcal{H}(R) - \mathcal{H}(S))/T) \right],$$ sodass eine „sortiertere“ Sequenz immer akzeptiert wird und eine „unsortiertere“ vor allem bei hohen Temperaturen. Wenn $R$ akzeptiert wird, gilt $S:=R$, ansonsten wir die alte Konfiguration $S$ weiter benutzt.
  5. Reduziere die Temperatur (beispielsweise durch Multiplikation mit einer Zahl etwas kleiner als 1) und breche ab, wenn die Zieltemperatur erreicht ist. Ansonsten beginne wieder bei Punkt 2.

Genug der Theorie: In einem Gist auf GitHub präsentiere ich ein schnell terminierendes Sortierprogramm, das zwar nicht immer eine sortierte Liste findet, aber zumindest eine Näherung! Es ist also Bogosort in mehr als nur einer Hinsicht überlegen!

Wer braucht da noch $\mathcal{O}(N \log(N))$ Sortier-Algorithmen?!

SHA-256 in 256 Zeilen

Programmiersprachen muss man üben, um sie zu lernen und um sie nicht wieder zu vergessen. Ich habe also meine Zeit damit vertrieben einen SHA-256 zu schreiben — eine kryptographische Hash Funktion. Die Spezifikation ist glücklicherweise sehr sehr verständlich. Und auch wenn es tausende andere Implementationen gibt, die schneller sind, alle Grenzfälle beachten (ich befürchte, dass mein Programm Probleme auf Big Endian Systemen bekommt), und sogar Schaltkreise, die hochoptimiert nur diese Operation beherrschen (Stichwort: Bitcoin ASIC), ist meiner dennoch sehenswert, da er SHA-256 in 256 Zeilen darstellt.

Der Code ist als Gist auf GitHub, da er in seinen 256 Zeilen ansonsten den Lesefluss stören würde.

In Python ist es übrigens etwas kürzer.

print(hashlib.sha256(b"Hallo Welt!").hexdigest())

DGLshow

Nachdem ich so vielen Differenzialgleichungssystemen [1, 2, 3, 4] begegnet bin, die sich nicht analytisch lösen lassen, habe ich mir ein Programm zur numerischen Lösung und Visualisierung derselben geschrieben.

Die grundlegende Idee zur numerischen Lösung von Differentialgleichungen ist es, die Zeit in diskreten Schritten $\tau$ vergehen zu lassen. Nach jedem Schritt wird der Zustand so geändert, als ob sich während des Zeitschrittes nichts geändert hätte und die „Kräfte“ werden entsprechend der Bewegungsgleichungen neu berechnet. Für infinitesimal kleine $\tau \to \mathrm{d}t$ ist diese Methode schließlich exakt.

Im einfachsten Fall, dem Euler Verfahren, sähe das für ein einfaches Fadenpendel nach $k$ Zeitschritten so aus \begin{align} \ddot\vartheta_{k+1} &= - mgl \sin(\vartheta_k)\ \dot\vartheta_{k+1} &= \tau \ddot\vartheta_{k} + \dot\vartheta_{k}\ \vartheta_{k+1} &= \tau \dot\vartheta_{k} + \vartheta_{k} \end{align} Unglücklicherweise hat dieses Verfahren ernsthafte Probleme mit der Energieerhaltung und braucht sehr kleine $\tau$ für brauchbare Ergebnisse. Es gibt deutlich ausgefeiltere Methoden, wie den klassischen Runge-Kutta Algorithmus. Es gibt Methoden, den Zeitschritt $\tau$ während der Simulation adaptiv anzupassen, um nur wenig Rechenaufwand in den wenig fehleranfälligen Phasen zu verbringen. Es gibt spezialisierte Methoden, die sehr gut für bestimmte Bewegungsgleichungen funktionieren, wie Velocity-Verlet, der oft für Molekulardynamiksimulationen eingesetzt wird.

Chaotische Systeme haben in der Regel etwas kompliziertere Bewegungsgleichungen. Das oben abgebildete Doppelpendel etwa wird, wie ich in einem anderen Post beschrieben habe durch folgendes Ungetüm beschrieben.

\begin{align} \ddot\theta_1 &= \frac{m_2 \cos(\theta_1 - \theta_2) (l_1 \sin(\theta_1 - \theta_2) \dot\theta_1^2 - g \sin(\theta_2)) + m_2 l_2 \sin(\theta_1 - \theta_2) \dot\theta_2^2 + (m_1 + m_2) g \sin(\theta_1)}{m_2 l_1 \cos^2(\theta_1 - \theta_2) - (m_1+m_2) l_1} \ \ddot\theta_2 &= \frac{m_2 l_2 \cos(\theta_1 - \theta_2) \sin(\theta_1 - \theta_2) \dot\theta_2^2 + (m_1+m_2) l_1 \sin(\theta_1 - \theta_2) \dot\theta_1^2 + (m_1+m_2) g \cos(\theta_1 - \theta_2) \sin(\theta_1) - (m_1+m_2) g \sin(\theta_2)}{(m_1+m_2) l_2 - m_2 l_2 \cos^2(\theta_1 - \theta_2)} \end{align}

Anfangs empfiehlt es sich also etwas einfacheres und vertrauteres zu lösen, wie den Lorenz-Attraktor \begin{align} \dot{X} &= a(Y - X) \ \dot{Y} &= X(b - Z) - Y \ \dot{Z} &= XY - cZ \ \end{align}

Oder das Dreikörperproblem \begin{align} \ddot{\vec{x}_1} &= -\frac{Gm_2}{\left(x_1 - x_2\right)^3} (\vec{x}_1 - \vec{x}_2) - \frac{Gm_3}{\left(x_1 - x_3\right)^3} (\vec{x}_1 - \vec{x}_3)\ \ddot{\vec{x}_2} &= -\frac{Gm_1}{\left(x_2 - x_1\right)^3} (\vec{x}_2 - \vec{x}_1) - \frac{Gm_3}{\left(x_2 - x_3\right)^3} (\vec{x}_2 - \vec{x}_3)\ \ddot{\vec{x}_3} &= -\frac{Gm_1}{\left(x_3 - x_1\right)^3} (\vec{x}_3 - \vec{x}_1) - \frac{Gm_2}{\left(x_3 - x_2\right)^3} (\vec{x}_3 - \vec{x}_2)\ \end{align}

Da man das 3-Körperproblem trivial auf ein $N$-Körperproblem erweitern kann, habe ich hier ein „Sonnensystem“ bzw. Bohrsches „Atom“-modell simuliert.

Sonnensystem

Um die obigen (bewegten) Bilder zu erzeugen und um ein bewegtes Doppelpendel für meinen Schreibtisch zu haben, — wennauch nur auf einem Bildschirm — habe ich in C++ einen adaptiven Runge-Kutta-4 Löser geschrieben, der mit den Qt Zeichenprimitiven animiert wird.

Auch wenn der Code nicht sehr aufgeräumt ist und Startwerte im Quellcode angepasst werden müssen, sind die Quellen auf GitHub: github.com/surt91/DGLshow.