C# :: Aufgabe #265

2 Lösungen Lösungen öffentlich

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ß!

Lösungen:

1x
vote_ok
von eulerscheZhl (5230 Punkte) - 03.11.2019 um 06:31 Uhr
Quellcode ausblenden C#-Code
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;
    }
}
1 Kommentar
vote_ok
von hollst (13980 Punkte) - 27.02.2020 um 12:01 Uhr
Quellcode ausblenden C#-Code
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;
                }
            }
        }
    }
}