C# :: Aufgabe #265
2 Lösungen
Das Zahnstochermuster und die Sequenz der Anzahl freier Spitzen im Muster
Fortgeschrittener - C#
von hollst
- 01.10.2019 um 18:15 Uhr
Man kann sogar mit Zahnstochern Mathematik bzw. Informatik betreiben!
Ein Zahnstochermuster wird schrittweise erzeugt: Im ersten Schritt legt man einen Zahnstocher
beliebig und flach auf eine 2D-Fläche ab. Ohne besondere Einschränkungen seien angenommen, dass jeder Zahnstocher
genau die Länge 1 (eins) hat und das erste Exemplar senkrecht und mittig in einem (X, Y)-Koordinatensystem
liegt. Das Muster hat nach dem ersten Schritt zwei freie Spitzen bei (1/2, 0) und (-1/2, 0).
Im zweite Schritt werden senkrecht zum ersten Zahnstocher auf die zwei freien Spitzen jeweils ein weiterer Zahnstocher gelegt,
danach haben wir vier freie Spitzen (siehe Bild step_2). Auf diese Weise wird schrittweise fortgefahren.
Die Sequenz der Anzahl freier Spitzen im Muster beginnt so:
Schritt........: 1..2..3..4..5....6..7..8..9..10 11 12 13 14 15 16 17 18 19 ...
freie Spitzen: 2..4..4..4..8..12, 8, 4, 8, 12, 12, 16, 28, 32, 16, 4, 8, 12, 12 ...
Sie steigt zunächst an bis 12 (Schritt 6), um danach auf 4 abzufallen (Schritt 8). Dieses Auf und Ab
zieht sich stetig fort (bis in alle Ewigkeit, was noch zu beweisen wäre [jedoch nicht von uns]).
Die Entwicklung des Zahnstochermusters hat fraktalen Charakter. Anhand von Bild step_10_14_17 soll dies belegt sein.
Die Programmieraufgabe bestehe darin, die Musterentwicklung bis zum Schritt 100 "interaktiv" darzustellen (Bild step_100)
und auch die Anzahl freier Zahnstocherspitzen jeweils anzugeben.
Viel Spaß!
Ein Zahnstochermuster wird schrittweise erzeugt: Im ersten Schritt legt man einen Zahnstocher
beliebig und flach auf eine 2D-Fläche ab. Ohne besondere Einschränkungen seien angenommen, dass jeder Zahnstocher
genau die Länge 1 (eins) hat und das erste Exemplar senkrecht und mittig in einem (X, Y)-Koordinatensystem
liegt. Das Muster hat nach dem ersten Schritt zwei freie Spitzen bei (1/2, 0) und (-1/2, 0).
Im zweite Schritt werden senkrecht zum ersten Zahnstocher auf die zwei freien Spitzen jeweils ein weiterer Zahnstocher gelegt,
danach haben wir vier freie Spitzen (siehe Bild step_2). Auf diese Weise wird schrittweise fortgefahren.
Die Sequenz der Anzahl freier Spitzen im Muster beginnt so:
Schritt........: 1..2..3..4..5....6..7..8..9..10 11 12 13 14 15 16 17 18 19 ...
freie Spitzen: 2..4..4..4..8..12, 8, 4, 8, 12, 12, 16, 28, 32, 16, 4, 8, 12, 12 ...
Sie steigt zunächst an bis 12 (Schritt 6), um danach auf 4 abzufallen (Schritt 8). Dieses Auf und Ab
zieht sich stetig fort (bis in alle Ewigkeit, was noch zu beweisen wäre [jedoch nicht von uns]).
Die Entwicklung des Zahnstochermusters hat fraktalen Charakter. Anhand von Bild step_10_14_17 soll dies belegt sein.
Die Programmieraufgabe bestehe darin, die Musterentwicklung bis zum Schritt 100 "interaktiv" darzustellen (Bild step_100)
und auch die Anzahl freier Zahnstocherspitzen jeweils anzugeben.
Viel Spaß!
Lösungen:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
class Program
{
public static void Main(string[] args)
{
Fractal fractal = new Fractal();
for (int i = 1; i <= 100; i++)
{
fractal.Expand();
Bitmap bmp = fractal.Draw();
bmp.Save("step_" + i + ".png");
bmp.Dispose();
}
}
}
class Toothpick : IEquatable<Toothpick>
{
public Point End1 { get; private set; }
public Point End2 { get; private set; }
public Toothpick(Point end1, Point end2)
{
this.End1 = end1;
this.End2 = end2;
}
public bool Equals(Toothpick pick)
{
return this.End1 == pick.End1 && this.End2 == pick.End2 ||
this.End1 == pick.End2 && this.End2 == pick.End1;
}
public override int GetHashCode()
{
return End1.GetHashCode() ^ End2.GetHashCode();
}
public IEnumerable<Toothpick> Expand()
{
Point center = new Point((End1.X + End2.X) / 2, (End1.Y + End2.Y) / 2);
int dx = (End1.X - End2.X) / 2;
int dy = (End1.Y - End2.Y) / 2;
yield return new Toothpick(new Point(End1.X + dy, End1.Y + dx), new Point(End1.X - dy, End1.Y - dx));
yield return new Toothpick(new Point(End2.X + dy, End2.Y + dx), new Point(End2.X - dy, End2.Y - dx));
}
}
class Fractal
{
public HashSet<Toothpick> Toothpicks { get; private set; } = new HashSet<Toothpick>();
private List<Toothpick> border = new List<Toothpick>();
public Fractal()
{
border.Add(new Toothpick(new Point(1, 0), new Point(-1, 0)));
}
public void Expand()
{
Toothpicks.UnionWith(border);
List<Toothpick> newBorder = new List<Toothpick>();
foreach (Toothpick pick in border)
{
foreach (Toothpick next in pick.Expand())
{
if (!Toothpicks.Contains(next))
{
Toothpick partner = newBorder.FirstOrDefault(p => p.Equals(next));
if (partner == null) newBorder.Add(next);
else newBorder.Remove(partner);
}
}
}
border = newBorder;
}
private Point TransformPoint(Point p, int xMin, int yMin, int factor)
{
return new Point(factor * (p.X - xMin + 1), factor * (p.Y - yMin + 1));
}
public Bitmap Draw()
{
List<Point> points = border.SelectMany(b => new[] { b.End1, b.End2 }).ToList();
int xMin = points.Min(p => p.X);
int xMax = points.Max(p => p.X);
int yMin = points.Min(p => p.Y);
int yMax = points.Max(p => p.Y);
int factor = 10;
Bitmap bmp = new Bitmap(factor * (xMax - xMin + 2), factor * (yMax - yMin + 2));
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.White);
foreach (Toothpick pick in Toothpicks)
g.DrawLine(Pens.Black, TransformPoint(pick.End1, xMin, yMin, factor), TransformPoint(pick.End2, xMin, yMin, factor));
foreach (Toothpick pick in border)
g.DrawLine(Pens.Blue, TransformPoint(pick.End1, xMin, yMin, factor), TransformPoint(pick.End2, xMin, yMin, factor));
}
return bmp;
}
}
using static System.Console;
using System.Collections.Generic;
using System.Text;
namespace Zahnstocher_Console
{
public static class Program
{
static void Main()
{
int max_generation = 25;
int laenge = 5;
List<Zahnstocher> lz = new List<Zahnstocher>();
Zahnstocher z = new Zahnstocher(0, 0, false, laenge);
lz.Add(z);
int i = max_generation;
WriteLine(); WriteLine($" max_generation: {i, 2}"); WriteLine();
ZahnstocherGrid zg = new ZahnstocherGrid(laenge, i);
WriteLine(zg.reference_grid.PrintIntArray(2));
for (var j = 0; j < zg.listing.Length; j++)
WriteLine($"{j, 3}: {zg.listing[j].Count,2}");
"ready".Info();
ReadKey();
}
static void Info(this string s) => WriteLine(s);
public static string PrintIntArray(this int[,] a, int space = 4)
{
StringBuilder sb = new StringBuilder();
int y = a.GetLength(0);
int x = a.GetLength(1);
for (var iy = 0; iy < y; iy++)
{
for (var ix = 0; ix < x; ix++)
{
string s = " ";
if(a[iy, ix] != 0)
s = $"{a[iy, ix], 2}";
while (s.Length < space)
s += " ";
sb.Append($"{s}");
}
sb.AppendLine();
}
return sb.ToString();
}
}
public class Zahnstocher
{
public int CenterX { private set; get; }
public int CenterY { private set; get; }
public int Laenge { private set; get; }
public bool Ausrichtung { private set; get; }
public Zahnstocher(int centerX, int centerY, bool ausrichtung_horizontal, int laenge)
{
this.CenterX = centerX;
this.CenterY = centerY;
this.Laenge = laenge;
this.Ausrichtung = ausrichtung_horizontal;
}
public Zahnstocher() { }
public Zahnstocher[] NextTwo(Zahnstocher t)
{
Zahnstocher[] result = new Zahnstocher[2];
for (var i = 0; i < result.Length; i++)
result[i] = new Zahnstocher();
int length = t.Laenge;
bool bo_dir = !t.Ausrichtung;
int lh = length / 2;
result[0].Laenge = length;
result[1].Laenge = length;
result[0].Ausrichtung = bo_dir;
result[1].Ausrichtung = bo_dir;
int y = t.CenterY;
int x = t.CenterX;
if (t.Ausrichtung)
{
result[0].CenterX = x - lh;
result[1].CenterX = x + lh;
result[0].CenterY = y;
result[1].CenterY = y;
}
else
{
result[0].CenterY = y - lh;
result[1].CenterY = y + lh;
result[0].CenterX = x;
result[1].CenterX = x;
}
return result;
}
}
public class ZahnstocherGrid
{
public List<Zahnstocher>[] listing;
public int Max_generations { private set; get; }
public int Toothpick_length { private set; get; }
readonly int offset_Y, offset_X;
readonly int grid_dimY, grid_dimX;
public int[,] reference_grid;
public ZahnstocherGrid(int toothpick_length, int max_generations)
{
this.Max_generations = max_generations + 1;
this.Toothpick_length = toothpick_length;
this.grid_dimY = 1 + (this.Max_generations / 2) * (this.Toothpick_length / 2) * 2;
this.grid_dimX = 1 + ((this.Max_generations - 1) / 2) * (this.Toothpick_length / 2) * 2;
this.offset_Y = grid_dimY / 2;
this.offset_X = grid_dimX / 2;
listing = new List<Zahnstocher>[Max_generations];
for (var i = 0; i < Max_generations; i++)
listing[i] = new List<Zahnstocher>();
this.reference_grid = new int[this.grid_dimY, this.grid_dimX];
Run();
}
private void Run()
{
bool ausrichtung_horizontal = false;
int centerY = 0, centerX = 0;
Zahnstocher z = new Zahnstocher(centerX, centerY, ausrichtung_horizontal, this.Toothpick_length);
listing[0].Add(z);
for (var i = 1; i < Max_generations; i++)
{
Adjust_RefMatrix(listing[i - 1]);
ausrichtung_horizontal = !ausrichtung_horizontal;
for (var j = 0; j < listing[i - 1].Count; j++)
{
Zahnstocher[] next = z.NextTwo(listing[i - 1][j]);
for (var k = 0; k < next.Length; k++)
{
int ny = next[k].CenterY + offset_Y;
int nx = next[k].CenterX + offset_X;
if(reference_grid[ny, nx] < 2)
listing[i].Add(next[k]);
}
}
}
}
private void Adjust_RefMatrix(List<Zahnstocher> liste)
{
for (var i = 0; i < liste.Count; i++)
{
int y = liste[i].CenterY;
int x = liste[i].CenterX;
int xx, yy;
int lh = Toothpick_length / 2;
for (var j = -lh; j <= lh; j++)
{
if (liste[i].Ausrichtung)
{
xx = x + j;
yy = y;
}
else
{
xx = x;
yy = y + j;
}
reference_grid[yy + offset_Y, xx + offset_X] += 1;
}
}
}
}
}
