Nach meinem letzten Ausflug in die Welt des Chaos und der nichtlinearen Systeme habe ich nun ein weiteres untersucht - und eine Frage ist immer noch offen...
Der Attractor wird durch folgende Gleichungen beschrieben:
Letztlich ist dieses System kein klassisch dynamisches sondern eher eine diskrete Map wie beispielsweise der Henon-Map.
Die Visualisierung erfolgt dabei letztlich durch eine diskrete Unterabtastung des kontimuierlichen Zustandsraumes und eine Akkumulation der Besuchshäufigkeiten jedes Punktes des Rasters. Um ein solches zweidimensionales Histogramm - denn um ein solches handelt es sich letztlich - mit einigem Gewinn darstellen zu können, hat sich eine Anzahl von Iterationen als günstig herausgestellt, die etwa der fünffachen Anzahl an Stützstellen im Raster entspricht. Bei einer Auflösung von 1600 mal 1200 Stützstellen - insgesamt 1 920 000 - also 10 000 000.
Mit dem Koordinatenursprung als Startpunkt und den folgenden Parametern
ergibt sich folgendes Aussehen des Histogramms (in grau das Ergebnis der Implementierung mittels C++, in Falschfarben das Ergebnis der Implementierung
mittels Java):
Darstellung des Systems als Ergebnis der Java-Implementierung
Darstellung des Systems als Ergebnis der C++-Implementierung
//angepasst aus https://paulbourke.net/fractals/clifford/paul_richards/main.cpp
/*
xn+1 = sin(a yn) + c cos(a xn)
yn+1 = sin(b xn) + d cos(b yn)
*/
<iostream>
<cmath>
<vector>
using namespace std;
// Change params only in this block
namespace {
const int width = 1600;
const int height = 1200;
const int frames = 1;
const int iters = 10000000;
const int skipIters = 10;
double sensitivity = 0.002;
const double minX = -4.0;
const double minY = minX * height / width;
const double maxX = 4.0;
const double maxY = maxX * height / width;
const double minA = acos( 1.6 / 2.0 );
const double maxA = acos( 1.3 / 2.0 );
const double minB = acos( -0.6 / 2.0 );
const double maxB = acos( 1.7 / 2.0 );
const double minC = acos( -1.2 / 2.0 );
const double maxC = acos( 0.5 / 2.0 );
const double minD = acos( 1.6 / 2.0 );
const double maxD = acos( 1.4 / 2.0 );
};
class Color {
public:
double r, g, b;
Color(const double &red = 0, const double &green = 0, const double &blue = 0) : r(red), g(green), b(blue) {
}
Color& operator+=(const Color &rhs) {
r += rhs.r;
g += rhs.g;
b += rhs.b;
return *this;
}
static Color createHue( double h ) {
h *= 6.0;
int hi = static_cast<int>( h );
double hf = h - hi;
switch( hi % 6 ) {
case 0:
return Color( 1.0 , hf, 0.0 );
case 1:
return Color( 1.0 - hf, 1.0, 0.0 );
case 2:
return Color( 0.0 , 1.0, hf );
case 3:
return Color( 0.0, 1.0 - hf, 1.0 );
case 4:
return Color( hf, 0.0, 1.0 );
case 5:
return Color( 1.0, 0.0, 1.0 - hf );
}
return Color();
}
Color operator+(const Color &rhs) const {
return Color(*this) += rhs;
}
};
int main(void) {
vector<Color> image( width * height );
const double a=1.7;
const double b=1.7;
const double c=0.6;
const double d=1.2;
const Color curCol = Color::createHue(0);
double x = 0.0, y = 0.0;
double xn = 0.0, yn = 0.0;
int xi = 0, yi = 0;
for (int j = 0; j < iters; ++j) {
xn = sin(a * y) + c * cos(a * x);
yn = sin(b * x) + d * cos(b * y);
x = xn;
y = yn;
if ( j < skipIters )
continue;
xi = static_cast<int>( (x - minX) * width / (maxX - minX) );
yi = static_cast<int>( (y - minY) * height / (maxY - minY) );
if ( xi >= 0 && xi < width &&
yi >= 0 && yi < height ) {
image[ xi + yi * width ] += curCol;
}
}
clog << "\r";
clog << "\n";
cout
<< "P6\n"
<< width << " " << height << "\n"
<< "255\n";
double minr=100000000;
double maxr=-100000000;
double ming=100000000;
double maxg=-100000000;
double minb=100000000;
double maxb=-100000000;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Color &c = image[ x + y * width ];
if(c.r>maxr)
maxr=c.r;
if(c.r<minr)
minr=c.r;
if(c.g>maxg)
maxg=c.g;
if(c.g<ming)
ming=c.g;
if(c.b>maxb)
maxb=c.b;
if(c.b<minb)
minb=c.b;
}
}
unsigned char r=0;
unsigned char g=0;
unsigned char b=0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Color &c = image[ x + y * width ];
r = static_cast<unsigned char>( pow(c.r/maxr ,.15) * 255.0 );
g = static_cast<unsigned char>( pow(c.g/maxg ,.3) * 255.0 );
b = static_cast<unsigned char>( pow(c.b/maxb ,.3) * 255.0 );
cout << r << r << r;
}
}
clog << minr << "\n" << maxr << "\n" << ming << "\n" << maxg << "\n" << minb << "\n" << maxb << "\n";
return 0;
}
import de.netsysit.model.table.ColorGradientTableModel;
import de.netsysit.util.StopWatch;
import java.io.IOException;
import java.nio.charset.Charset;
public class Clifford
{
public static void main(String[] args) throws IOException
{
int width=1600;
int height=1200;
int iters=10000000;
int citers=width*height*10;
int skip=10;
double minX = -4.0;
double minY = minX * (double)height / (double)width;
double maxX = 4.0;
double maxY = maxX * (double)height / (double)width;
double gamma=0.2;
double a=1.7;
double b=1.7;
double c=0.6;
double d=1.2;
double[] image=new double[width*height];
double x = 0.0, y = 0.0;
double xn = 0.0, yn = 0.0;
StopWatch sw=new StopWatch(true,true);
for (int j = 0; j < iters; ++j)
{
xn = java.lang.Math.sin(a * y) + c * java.lang.Math.cos(a * x);
yn = java.lang.Math.sin(b * x) + d * java.lang.Math.cos(b * y);
x = xn;
y = yn;
if ( j < skip )
continue;
int xi = (int)( (x - minX) * width / (maxX - minX) );
int yi = (int)( (y - minY) * height / (maxY - minY) );
if ( ((xi >= 0) && (xi < width)) && ((yi >= 0) && (yi < height)) )
{
image[ xi + yi * width ] += 1;
}
}
System.out.println(sw.measure());
double minr= Double.MAX_VALUE;
double maxr= Double.MIN_VALUE;
double cr=0;
for (int j = 0; j < image.length; ++j)
{
cr = image[j];
if (cr > maxr)
maxr = cr;
if (cr < minr)
minr = cr;
}
ColorGradientTableModel cgtm=new ColorGradientTableModel(ColorGradientTableModel.Types.MAGMA);
java.awt.Color color=null;
java.io.FileOutputStream fos=new java.io.FileOutputStream("/tmp/clifford.ppm");
java.io.BufferedOutputStream pw=new java.io.BufferedOutputStream(fos);
java.lang.String head="P6\n"+width + " " + height+"\n255\n";
pw.write(head.getBytes(Charset.forName("US-ASCII")));
for (int j = 0; j < image.length; ++j)
{
cr = image[j];
double transformed= java.lang.Math.pow(cr / maxr, gamma);
color=cgtm.computeColor((float)transformed,false);
pw.write(color.getRed());
pw.write(color.getGreen());
pw.write(color.getBlue());
}
pw.close();
fos.close();
System.out.println(sw.measure());
}
}
Der eine interessante Fakt, dessen Ursachen sich mir bisher nicht erschlossen haben ist die Tatsache, dass die pure Berechnung (abzüglich der Erzeugung des Ergebnisbildes) in Java deutlich schneller ist als in der C++-Variante: Die C++-Variante benötigt im Schnitt etwas über 700ms, während sich die Java-Variante mit im Schnitt unter 600ms zufriedengibt. Eigentlich kann das nur daran liegen, dass die Histogrammberechnung mit dem überladenen Operator in der Klasse Color die Performanz zunichte macht. Ich werde das vielleicht später nochmals analysieren...
Interessant könnte auch ein Test sein, in dem die Parameter stückweise variiert werden und man sich eine Animation des Ergebnisses ansehen kann...
29.03.2025
Ich berichtete hier bereits über Experimente mit dem Clifford-Attractor, allerdings war ich noch Experimente unter geringfügig geänderten Parametern schuldig...
Ticketsysteme sind lebende Wesen
29.03.2020
Hier zunächst wieder eine Triggerwarnung: Dieser Artikel wird meine Meinung abbilden. es kann sein, dass sie dem einen oder anderen nicht gefällt - das ist mir aber egal. Und wenn hier irgendwelche Schneeflocken mitlesen, dann sind die selber schuld.
Weiterlesen...Android Basteln C und C++ Chaos Datenbanken Docker dWb+ ESP Wifi Garten Geo Go GUI Gui Hardware Java Jupyter JupyterBinder Komponenten Links Linux Markdown Markup Music Numerik OpenSource PKI-X.509-CA Präsentationen Python QBrowser Rants Raspi Revisited Security Software-Test sQLshell TeleGrafana Verschiedenes Video Virtualisierung Windows Upcoming...
Ich berichtete hier bereits über Experimente mit dem Clifford-Attractor, allerdings war ich noch Experimente unter geringfügig geänderten Parametern schuldig...
WeiterlesenEs wurde wieder einmal Zeit für ein neues Feature in meinem Static Site Generator mittels dessen ich ja auch meine Heimatseite im Zwischennetz gestalte und verwalte...
WeiterlesenEs kamen mehrere Faktoren zusammen: die Tatsache, dass ich nicht mehr ganz so kürzlich die 50 überschritten habe hatte ebenso darauf Einfluss wie das heutige trübe Wetter und auch der Fakt, dass ich bereits beinahe alle Wochenendpflichten erledigt habe. Der letzte Stein des Anstoßes war dann aber, dass sich heute zum 125. Mal der Geburtstag von Erich Fromm jährt.
WeiterlesenManche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.
Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.
Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...
PS: Meine öffentlichen Codeberg-Repositories findet man hier.