Skip to content

Latest commit

 

History

History
320 lines (221 loc) · 8.06 KB

rekursion.md

File metadata and controls

320 lines (221 loc) · 8.06 KB

Rekursion

Ein Sierpinski-Dreieck ist ein Fraktal, welches mit unterschiedlicher Tiefe gezeichnet werden kann. Folgende Animation zeigt wie sich das Fraktal ändert, wenn die Tiefe erhöht wird:

Sierpinski-Dreieck Animation

Schreiben Sie eine Funktion, die ein solches Sierpinski-Dreieck zeichnet.

function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)

Die ersten 6 Parameter stellen dabei die drei Eckpunkte des Dreiecks dar: p0 = (x0, y0), p1 = (x1, y1), p2 = (x2, y2) Der letzte Parameter beschreibt, wie tief das Fraktal noch gehen soll.

Bei depth=0 soll ein solides Dreieck gezeichnet werden. Bei depth=1 soll an jeder Ecke ein kleineres Dreieck gezeichnet werden, welche sich an den Mittelpunkten der Seitenlinien berühren.

Die Funktion soll sich selbst mit den Koordinaten eines kleineren Dreiecks aufrufen.

Nutzen Sie folgenden Code um die Funktion zu Testen.

var cw = canvas.width(), ch = canvas.height();
drawSierpinski(0.5*cw, 0.2*ch, 
               0.1*cw, 0.8*ch,
               0.9*cw, 0.8*ch,
               depth=6);

Tipps

Tipp 0: Abbruchbedingung

Überlegen Sie bei welcher Bedingung sich die Funktion nicht weiter selbst aufruft.

Lösung

depth == 0 oder depth < 1 oder ähnliches

Tipp 1: Rekursionsanfang

Überlegen Sie sich, was getan werden soll, wenn die Abbruchbedingung erfüllt ist.

Hinweis

Denken Sie dran, was oben bei depth=0 erwähnt worden ist.

Lösung

Füllen des kompletten Dreiecks.

Tipp 2: Zeichnen eines Dreiecks

Die Funktion canvas.fillArea zeichnet ein Polygon. Die Funktion übernimmt als einzigen Parameter ein Array mit beliebiger Länge. Jedes Element ist wiederum ein Array, welches 2 Elemente besitzt: eine x und eine y Koordinate. Dies sind jeweils die Eckpunkte des Polygons.

Lösung
canvas.fillArea([[x0, y0], [x1, y1], [x2, y2]]);
Tipp 3: Rekursionsschritt

Überlegen Sie wie oft sich die Funktion direkt selbst aufruft.

Lösung

Drei mal für alle drei Ecken

Tipp 4: Zerlegung des Dreiecks

Überlegen Sie sich, wie die Eckpunkte der kleineren Dreiecke an den Ecken berechnet werden.

Hinweis 0

Ein Eckpunkt ist gleichzeitig immer ein Eckpunkt des größeren Dreiecks.

Hinweis 1

Die beiden anderen Eckpunkte sind die Mittelpunkte der Strecken zwischen diesem Punkt und jeweils einem der beiden anderen Punkte. (Berechnung: Siehe Tipp 5)

Tipp 5: Mittelpunkt bestimmen

Überlegen Sie, wie man den Mittelwert von zwei Zahlen bestimmt.

Hinweis

Berechnen Sie jeweils den Mittelwert der beiden x Koordinaten und den Mittelwert der beiden y Koordinaten.

Lösung

Mögliche Lösungen sind

  • (x0+x1)/2, (y0+y1)/2
  • x0 + (x1-x0)/2, y0 + (y1-y0)/2
  • 0.5*x0 + 0.5*x1, 0.5*y0 + 0.5*y1

Schreiben Sie die Koordinaten der Mittelpunkte in Variablen, denn Sie werden diese mehrmals gebrauchen. Sie werden jeden dieser Werte zwei mal nutzen, denn ein Dreieck berührt ein anderes Dreieck in diesem Punkt.


Musterlösung

Lösung 0
function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)
{
	if depth <= 0 then
	{
		canvas.fillArea([[x0, y0], [x1, y1], [x2, y2]]);
	}
	else
	{
		var x01	= (x0+x1)/2, y01 = (y0+y1)/2; # Mittelpunkt zwischen p0 und p1
		var x02	= (x0+x2)/2, y02 = (y0+y2)/2; # Mittelpunkt zwischen p0 und p2
		var x12	= (x1+x2)/2, y12 = (y1+y2)/2; # Mittelpunkt zwischen p1 und p2
		
		drawSierpinski(x0, y0, x01, y01, x02, y02, depth-1);
		drawSierpinski(x1, y1, x01, y01, x12, y12, depth-1);
		drawSierpinski(x2, y2, x02, y02, x12, y12, depth-1);
	}
}
Lösung 1
function mix(z, a, b)
{
	# Äquivalent zu a*(1-z) + b*z
	return a + (b-a)*z;
}

function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)
{
	if depth <= 0 then
	{
		canvas.fillArea([[x0, y0], [x1, y1], [x2, y2]]);
	}
	else
	{
		var x01	= mix(0.5, x0, x1), y01 = mix(0.5, y0, y1); # Mittelpunkt zwischen p0 und p1
		var x02	= mix(0.5, x0, x2), y02 = mix(0.5, y0, y2); # Mittelpunkt zwischen p0 und p2
		var x12	= mix(0.5, x1, x2), y12 = mix(0.5, y1, y2); # Mittelpunkt zwischen p1 und p2
		
		drawSierpinski(x0, y0, x01, y01, x02, y02, depth-1);
		drawSierpinski(x1, y1, x01, y01, x12, y12, depth-1);
		drawSierpinski(x2, y2, x02, y02, x12, y12, depth-1);
	}
}

Probieren Sie einzelne 0.5 mit anderen Werten Zu ersetzen.

Beispiel

Ersetzt man y02 = mix(0.5, y0, y2) mit y02 = mix(0.6, y0, y2) entsteht ein unvollkommenes Sierpinski-Dreieck, welches ein bisschen gekrümmt ist.

Verändertes Sierpinski-Dreieck

Verändertes Sierpinski-Dreieck

Gesamtes Programm
function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)
{
	if depth <= 0 then
	{
		canvas.fillArea([[x0, y0], [x1, y1], [x2, y2]]);
	}
	else
	{
		var x01	= (x0+x1)/2, y01 = (y0+y1)/2; # Mittelpunkt zwischen p0 und p1
		var x02	= (x0+x2)/2, y02 = (y0+y2)/2; # Mittelpunkt zwischen p0 und p2
		var x12	= (x1+x2)/2, y12 = (y1+y2)/2; # Mittelpunkt zwischen p1 und p2

		drawSierpinski(x0, y0, x01, y01, x02, y02, depth-1);
		drawSierpinski(x1, y1, x01, y01, x12, y12, depth-1);
		drawSierpinski(x2, y2, x02, y02, x12, y12, depth-1);
	}
}

var cw = canvas.width(), ch = canvas.height();
for var i in 0:10 do
{
	canvas.setFillColor(1, 1, 1);
	canvas.clear();

	canvas.setFillColor(0, 0, 0);
	drawSierpinski(0.5*cw, 0.2*ch, 
				   0.1*cw, 0.8*ch,
				   0.9*cw, 0.8*ch,
				   depth=i);

	wait(700);
}

Schrittweise Implementierung

Schritt 0: Leerer Funktionskörper
function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)
{

}
Schritt 1: Abbruchbedingung
function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)
{
	if depth <= 0 then
	{
	
	}
	else
	{
	
	}
}
Schritt 2: Rekursionsanfang
function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)
{
	if depth <= 0 then
	{
		canvas.fillArea([[x0, y0], [x1, y1], [x2, y2]]);
	}
	else
	{
	
	}
}
Schritt 3: Mittelpunkte bestimmen
function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)
{
	if depth <= 0 then
	{
		canvas.fillArea([[x0, y0], [x1, y1], [x2, y2]]);
	}
	else
	{
		var x01	= (x0+x1)/2, y01 = (y0+y1)/2; # Mittelpunkt zwischen p0 und p1
		var x02	= (x0+x2)/2, y02 = (y0+y2)/2; # Mittelpunkt zwischen p0 und p2
		var x12	= (x1+x2)/2, y12 = (y1+y2)/2; # Mittelpunkt zwischen p1 und p2
	}
}
Schritt 4: Rekursionsschritt
function drawSierpinski(x0, y0, x1, y1, x2, y2, depth=6)
{
	if depth <= 0 then
	{
		canvas.fillArea([[x0, y0], [x1, y1], [x2, y2]]);
	}
	else
	{
		var x01	= (x0+x1)/2, y01 = (y0+y1)/2; # Mittelpunkt zwischen p0 und p1
		var x02	= (x0+x2)/2, y02 = (y0+y2)/2; # Mittelpunkt zwischen p0 und p2
		var x12	= (x1+x2)/2, y12 = (y1+y2)/2; # Mittelpunkt zwischen p1 und p2
		
		drawSierpinski(x0, y0, x01, y01, x02, y02, depth-1);
		drawSierpinski(x1, y1, x01, y01, x12, y12, depth-1);
		drawSierpinski(x2, y2, x02, y02, x12, y12, depth-1);
	}
}