diff --git a/Aufgabe2-Rechenrätsel/Cargo.toml b/Aufgabe2-Rechenraetsel/Cargo.toml similarity index 100% rename from Aufgabe2-Rechenrätsel/Cargo.toml rename to Aufgabe2-Rechenraetsel/Cargo.toml diff --git a/Aufgabe2-Rechenrätsel/doc.tex b/Aufgabe2-Rechenraetsel/doc.tex similarity index 100% rename from Aufgabe2-Rechenrätsel/doc.tex rename to Aufgabe2-Rechenraetsel/doc.tex diff --git a/Aufgabe2-Rechenrätsel/ergebnis-latex.sh b/Aufgabe2-Rechenraetsel/ergebnis-latex.sh similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnis-latex.sh rename to Aufgabe2-Rechenraetsel/ergebnis-latex.sh diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/1.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/1.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/1.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/1.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/10.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/10.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/10.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/10.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/11.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/11.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/11.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/11.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/12.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/12.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/12.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/12.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/13.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/13.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/13.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/13.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/14.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/14.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/14.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/14.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/15.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/15.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/15.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/15.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/16.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/16.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/16.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/16.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/17.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/17.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/17.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/17.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/18.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/18.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/18.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/18.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/19.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/19.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/19.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/19.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/2.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/2.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/2.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/2.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/20.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/20.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/20.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/20.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/21.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/21.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/21.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/21.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/22.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/22.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/22.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/22.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/23.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/23.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/23.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/23.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/24.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/24.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/24.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/24.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/25.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/25.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/25.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/25.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/26.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/26.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/26.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/26.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/3.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/3.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/3.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/3.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/4.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/4.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/4.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/4.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/5.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/5.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/5.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/5.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/6.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/6.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/6.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/6.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/7.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/7.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/7.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/7.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/8.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/8.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/8.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/8.txt diff --git a/Aufgabe2-Rechenrätsel/ergebnisdateien/9.txt b/Aufgabe2-Rechenraetsel/ergebnisdateien/9.txt similarity index 100% rename from Aufgabe2-Rechenrätsel/ergebnisdateien/9.txt rename to Aufgabe2-Rechenraetsel/ergebnisdateien/9.txt diff --git a/Aufgabe2-Rechenrätsel/rust-toolchain b/Aufgabe2-Rechenraetsel/rust-toolchain similarity index 100% rename from Aufgabe2-Rechenrätsel/rust-toolchain rename to Aufgabe2-Rechenraetsel/rust-toolchain diff --git a/Aufgabe2-Rechenrätsel/src/main.rs b/Aufgabe2-Rechenraetsel/src/main.rs similarity index 100% rename from Aufgabe2-Rechenrätsel/src/main.rs rename to Aufgabe2-Rechenraetsel/src/main.rs diff --git a/Aufgabe3-HexMax/tests/test.rs b/Aufgabe3-HexMax/tests/test.rs index 3d34cd6..b25024c 100644 --- a/Aufgabe3-HexMax/tests/test.rs +++ b/Aufgabe3-HexMax/tests/test.rs @@ -22,34 +22,38 @@ fn test_2() { #[test] fn test_3() { test("beispieldaten/hexmax3.txt", - concat!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FAA98BB8B9DFAFEAE888DD888AD8BA8EA8888")); + concat!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFAA98BB8B9DFAFEAE888DD888AD8BA8EA8888")); } #[test] fn test_4() { test("beispieldaten/hexmax4.txt", - concat!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB8DE88BAA8ADD888898E9BA8", - "8AD98988F898AB7AF7BDA8A61BA7D4AD8F888")); +concat!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB8DE88BAA8A", +"DD888898E9BA88AD98988F898AB7AF7BDA8A61BA7D4AD8F888")); } #[test] fn test_5() { test("beispieldaten/hexmax5.txt", - concat!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88EFA9EBE89EFA99FBDAA8", - "E8EAD88AB899F8E8F9AA9E9AD88988EDA9A99888EDAD989A8BAFD8A88888888", - "888888888888888888888888888888888888888888888888888888888888888", - "888888888888888888888888888888888888888888888888888888888888888", - "888888888888888888888888888888888888888888888888888888888888888", - "8888888888888888888888888888888888888888888888888888888")); +concat!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", +"FFFFFFFFFFFFFFFFFFFFF88EFA9EBE89EFA99FBDAA8E8EAD88", +"AB899F8E8F9AA9E9AD88988EDA9A99888EDAD989A8BAFD8A88", +"88888888888888888888888888888888888888888888888888", +"88888888888888888888888888888888888888888888888888", +"88888888888888888888888888888888888888888888888888", +"88888888888888888888888888888888888888888888888888", +"88888888888888888888888888888888888888888888888888")); } diff --git a/Schiebeparkplatz.png b/Schiebeparkplatz.png new file mode 100644 index 0000000..27f9726 Binary files /dev/null and b/Schiebeparkplatz.png differ diff --git a/doc.tex b/doc.tex new file mode 100644 index 0000000..692202a --- /dev/null +++ b/doc.tex @@ -0,0 +1,778 @@ +\documentclass[a4paper,12pt,ngerman]{scrartcl} +\usepackage{babel} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[a4paper,margin=2.5cm,footskip=2cm]{geometry} +\DeclareUnicodeCharacter{25CB}{$\circ$} + +% Kopf- und Fußzeilen +\usepackage{scrlayer-scrpage, lastpage} +\setkomafont{pageheadfoot}{\large\textrm} +\cfoot*{\thepage{}/\pageref{LastPage}} + +% Position des Titels +\usepackage{titling} +\setlength{\droptitle}{-1.0cm} + +% Für mathematische Befehle und Symbole +\usepackage{amsmath} +\usepackage{amssymb} + +% Für Bilder +\usepackage{graphicx} + +% Für Algorithmen +\usepackage{algpseudocode} + +% Für Quelltext +\usepackage{minted} +\usepackage{color} +\definecolor{mygreen}{rgb}{0,0.6,0} +\definecolor{mygray}{rgb}{0.5,0.5,0.5} +\definecolor{mymauve}{rgb}{0.58,0,0.82} + +% Anführungszeichen +\usepackage{csquotes} +\usepackage{forloop} + +% Diese beiden Pakete müssen zuletzt geladen werden +\usepackage{hyperref} % Anklickbare Links im Dokument +\usepackage{cleveref} + +% Daten für die Titelseite +\title{40. Bundeswettbewerb Informatik} +\author{Marcel Zinkel} + +\newcommand{\beispiel}[1] { + hexmax#1.txt: + \inputminted[breaklines,breakanywhere]{text}{Aufgabe3-HexMax/ergebnisdateien/#1.txt} +} + +\newcommand{\rechen}[1] { + \inputminted[breaklines]{text}{Aufgabe2-Rechenraetsel/ergebnisdateien/#1.txt} +} + +\begin{document} + +\maketitle + +Der Bundeswettbewerb Informatik ist ein seit 1980 jedes Jahr stattfindender +Schülerwettbewerb, der die Schüler für die Informatik begeistern soll. Dabei +soll die Teilnehmer ein Programm entwickeln, das das in der Aufgabenstellung +beschriebene Problem löst. Außerdem soll der Lösungsweg dokumentiert werden. Der +Wettbewerb wird in drei Runden ausgetragen. Die Aufgaben der ersten beiden +Runden werden zu Hause bearbeitet. Die besten der zweite Runde werden zur +Endrunde eingeladen, die bei einem jährlich wechselnden Sponsor ausgerichtet +wird. Weitere Informationen zum Bundeswettbewerb Informatik kann man auf der +\href{https://bwinf.de/bundeswettbewerb/}{offiziellen Webseite} erhalten. + +Ich habe dieses Jahr auch teilgenommen und im Folgenden kann meine Einsendungen +der ersten beiden Runden finden. Dabei habe ich beim Zusammenstellen noch +geringfügige Überarbeitungen vorgenommen und einige Beispiele entfernt, damit +die Arbeit übersichtlicher wird. + +\tableofcontents + +\vspace{0.5cm} + +\part{Runde 1} +In der ersten Runde musste man mindestens drei von fünf Aufgaben bearbeiten, wobei die +besten drei gewertet werden. Ich habe die Aufgabe 1, 2 und 4 bearbeitet und mit +15 Punkten die Höchstpunktzahl erreicht und mich damit für die zweite Runde +qualifiziert. Der Quelltext meiner Programme, die Aufgabenstellung und weitere +Beispielausgaben findet man in meinem +\href{https://git.zinkel.org/MrGeorgen/bwinf40-runde1}{Git-Repository}. + +\section{Aufgabe 1: Schiebeparkplatz} +Auf einen Parkplatz parken die Autos senkrecht zur Straße. Vor diesen Autos +parken außerdem noch welche parallel zur Straße. Im folgenden Bild kann solch einen +Parkplatz sehen. +\begin{center} + \includegraphics{Schiebeparkplatz.png} +\end{center} +Die quer parkenden Autos können verschoben werden, damit die Autos, die dahinter +parken, rausfahren können. Es soll ein Programm geschrieben werden, das bei +einer gegebenen Parkplatzsituation für jedes normal parkende Auto ausgibt, +wie die quer parkenden Autos verschoben werden müssen, damit es ausfahren kann. +Dabei sollen möglichst wenige Autos verschoben werden. + +\subsection{Lösungsidee} +Für jede normale Textdatei wird die ASCII-Codierung verwendet bzw. ein +Zeichensatz, der auf ASCII basiert und zusätzliche Sonderzeichen enthält, wobei +die Codes, die bereits in ASCII enthalten sind, übernommen wurden. Die +ASCII-Codes für die Buchstaben sind entsprechend dem Alphabet hintereinander +angeordnet. Dies ist sehr nützlich, da somit eine Liste der Buchstaben und deren +Position im Alphabet nicht benötigt wird. So kann der Buchstabe des nächsten +einfach bestimmt werden. + +Zur Lösung des Parkplatzproblems werden die Autos, wenn möglich nach links bzw. +rechts verschoben, bis die Parklücke frei ist. Je nachdem, in welche Richtung +weniger Autos verschoben werden müssen, wird die Verschiebungsrichtung gewählt. + +\subsection{Umsetzung} +Da die Lösung des Problems keine besondere Library benötigt und die Anforderungen +an die Ausführungsgeschwindigkeit gering sind, hätte man fast jede Programmiersprache +wählen können. Ich habe mich für Go entschieden. + +Das Programm akzeptiert als Argumente eine beliebige Anzahl an Pfaden zu Parkplatzdateien. +Für das Iterieren über die Argumente wird eine for-Schleife benutzt. +Innerhalb der Schleife wird die Eingabedatei ausgelesen. Anhand des ersten und letzten +Buchstabens der Autos auf den normalen Parkplätzen wird die Anzahl an Parkplätzen +bestimmt. Anschließend wird eine Slice\footnote{ein dynamisch vergrößerbares Array in Go} +mit den Buchstaben der quer parkenden Autos befüllt an den Indexen, an denen sie die +normalen Parkplätze blockieren. + +In einer for-Schleife wird die Funktion \mintinline{go}|move| je Index der Parkplätze doppelt +für beide Verschiebungsrichtungen aufgerufen. +In der Funktion \mintinline{go}|move| wird ein Auto verschoben, wenn nicht das Ende des +Parkplatzes erreicht ist. Die Funktion ruft sich rekursiv auf, um weitere Autos zu +verschieben. Je nachdem, in welche Richtung weniger Autos verschoben werden müssen, +wird sich für das Verschieben in diese Richtung entschieden. +Wenn es allerdings gleich viele sind, ist die geringere Anzahl der verschobenen +Plätze aller Autos ausschlaggebend für das Verschieben in diese Richtung. + +\subsection{Beispiele} +Zunächst habe ich das Programm mit dem vorhin abgebildeten Parkplatz +ausprobiert: +\begin{minted}[]{text} +beispieldaten/parkplatz0.txt: +A: +B: +C: H 1 rechts +D: H 1 links +E: +F: H 1 links, I 2 links +G: I 1 links +\end{minted} +Diese Ausgabe entspricht der Lösung, die in der Aufgabenstellung gegeben wurde. + +Außerdem habe ich selbst noch weitere Testfälle erstellt, die man unter +\enquote{a1-Schiebeparkplatz/testdaten} finden kann. +Als Erstes habe ich getestet, ob das Programm auch mit dem Verschieben +sehr vieler Autos klarkommt. Es gibt 26 Parkplätze, wovon alle außer die ersten +beiden mit quer parkenden Autos blockiert sind. Für die quer parkenden Autos +wurden Kleinbuchstaben verwendet, da alle Großbuchstaben schon belegt sind. +\begin{minted}[breaklines]{text} +testdaten/test0.txt: +A: +B: +C: a 2 links +D: a 1 links +E: a 2 links, b 2 links +F: a 1 links, b 1 links +G: a 2 links, b 2 links, c 2 links +H: a 1 links, b 1 links, c 1 links +I: a 2 links, b 2 links, c 2 links, d 2 links +J: a 1 links, b 1 links, c 1 links, d 1 links +K: a 2 links, b 2 links, c 2 links, d 2 links, e 2 links +L: a 1 links, b 1 links, c 1 links, d 1 links, e 1 links +M: a 2 links, b 2 links, c 2 links, d 2 links, e 2 links, f 2 links +N: a 1 links, b 1 links, c 1 links, d 1 links, e 1 links, f 1 links +O: a 2 links, b 2 links, c 2 links, d 2 links, e 2 links, f 2 links, g 2 links +P: a 1 links, b 1 links, c 1 links, d 1 links, e 1 links, f 1 links, g 1 links +Q: a 2 links, b 2 links, c 2 links, d 2 links, e 2 links, f 2 links, g 2 links, h 2 links +R: a 1 links, b 1 links, c 1 links, d 1 links, e 1 links, f 1 links, g 1 links, h 1 links +S: a 2 links, b 2 links, c 2 links, d 2 links, e 2 links, f 2 links, g 2 links, h 2 links, i 2 links +T: a 1 links, b 1 links, c 1 links, d 1 links, e 1 links, f 1 links, g 1 links, h 1 links, i 1 links +U: a 2 links, b 2 links, c 2 links, d 2 links, e 2 links, f 2 links, g 2 links, h 2 links, i 2 links, j 2 links +V: a 1 links, b 1 links, c 1 links, d 1 links, e 1 links, f 1 links, g 1 links, h 1 links, i 1 links, j 1 links +W: a 2 links, b 2 links, c 2 links, d 2 links, e 2 links, f 2 links, g 2 links, h 2 links, i 2 links, j 2 links, k 2 links +X: a 1 links, b 1 links, c 1 links, d 1 links, e 1 links, f 1 links, g 1 links, h 1 links, i 1 links, j 1 links, k 1 links +Y: a 2 links, b 2 links, c 2 links, d 2 links, e 2 links, f 2 links, g 2 links, h 2 links, i 2 links, j 2 links, k 2 links, l 2 links +Z: a 1 links, b 1 links, c 1 links, d 1 links, e 1 links, f 1 links, g 1 links, h 1 links, i 1 links, j 1 links, k 1 links, l 1 links +\end{minted} +Die Autos wurden abwechselnd jeweils um ein oder zwei Parkplätze verschoben, +je nachdem ob das ausfahrende Auto hinter der linken oder rechten Hälfe des quer +stehenden Autos steht. + +Die Bezeichner der Autos müssen auch nicht bei A beginnen. Der Beginn mit +anderen Buchstaben ist möglich. Dies zeigt das folgende Beispiel an der +Eingabedatei \enquote{parkplatz3.txt} mit veränderten Bezeichnern. +\begin{minted}[]{text} +testdaten/test1.txt: +C: +D: X 1 rechts +E: X 1 links +F: +G: Y 1 rechts +H: Y 1 links +I: +J: +K: Q 2 links +L: Q 1 links +M: Q 2 links, R 2 links +N: Q 1 links, R 1 links +O: Q 2 links, R 2 links, S 2 links +P: Q 1 links, R 1 links, S 1 links +\end{minted} + +\section{Aufgabe 2: Vollgeladen} +Es sind Hotels mit der Entfernung vom Start und einer Bewertung gegebenen. Man +soll ein Programm schreiben, dass eine Route zu einem Ziel über diese Hotels +bildet. Dabei darf an einem Tag nur sechs Stunden gefahren werden und das Ziel +muss innerhalb fünf Tagen erreicht werden. Dabei soll die niedrigste Bewertung +eines angesteuerten Hotels möglichst hoch sein. + +\subsection{Lösungsidee} +Da die niedrigste Bewertung möglichst hoch sein soll, sollte zuerst versucht +eine Route mit einer Mindestbewertung von 5.0 zu bilden. Scheitert dies wird +es mit 4.9, 4.8, ..., 0.1 versucht. Die Routenbildung mit einer bestimmten +Mindestbewertung kann aus zwei Gründen fehlschlagen: +\begin{itemize} + \item Innerhalb der 6 Stunden Fahrzeit gibt es kein Hotel mit der + Mindestbewertung. + \item an mehr als 4 Hotels muss gehalten werden +\end{itemize} + +\subsection{Umsetzung} +Die Lösungsidee wird in C implementiert. Eine Hilfsfunktion der +\href{https://git.zinkel.org/MrGeorgen/advanced_C_standard_library}{advanced +C standard library}, +die Textdateien ausliest, wird außerdem verwendet. +Das Programm erhält als Argumente eine beliebige Anzahl Dateipfade +zu Eingabedateien. Mit einer for-Schleife wird über die Dateipfade iteriert. In +der Schleife wird die Eingabedatei ausgelesen. Die Anzahl der Hotels und die +Gesamtfahrzeit werden jeweils in einer Variable abgespeichert. Anschließend wird +ein Array mit Strukturen, die Hotels darstellen, befüllt. In einer +do-while-Schleife wird mit einer Bewertung von 5.0, 4.9, ..., 0.1 versucht eine +Route zu bilden. Im Programm werden die Bewertungen als Integer dargestellt, +wobei die Bewertung der Eingabedatei mit 10 multipliziert wird. Die Ausgabe ist +wiederum im ursprünglichen Format. Wenn dies bei einer Bewertung erfolgreich +ist, wird die Schleife beendet. In dieser Schleife ist wiederum eine +for-Schleife, die über die Hotels iteriert, worin das letzte Hotel gespeichert +wird, das mindestens die angestrebte Bewertung hat. Ist das Hotel allerdings +mehr als 6 Stunden vom letzten Haltepunkt entfernt, muss das letzte Hotel +verwendet werden, an dem das Halten möglich ist. Es sei denn es gibt kein Hotel +mit der entsprechenden Bewertung oder es wurde bereits an mehr als drei Hotels +gehalten. Dann muss mit der nächsten Bewertung fortgefahren werden. Nachdem eine +Route gefunden wurde, wird diese ausgegeben und eventuell mit der nächsten +Eingabedatei fortgefahren. + +\subsection{Beispiele} +Bei den folgenden Lösungen der gegebenen Beispiele steht jede Zeile für ein +Hotel, in dem übernachtet wird. +\begin{minted}[]{text} +hotels1.txt: +Entfernung vom Start: 347 Minuten, Bewertung: 2.7 +Entfernung vom Start: 687 Minuten, Bewertung: 4.4 +Entfernung vom Start: 1007 Minuten, Bewertung: 2.8 +Entfernung vom Start: 1360 Minuten, Bewertung: 2.8 +hotels2.txt: +Entfernung vom Start: 341 Minuten, Bewertung: 2.3 +Entfernung vom Start: 700 Minuten, Bewertung: 3.0 +Entfernung vom Start: 1053 Minuten, Bewertung: 4.8 +Entfernung vom Start: 1380 Minuten, Bewertung: 5.0 +hotels3.txt: +Entfernung vom Start: 360 Minuten, Bewertung: 1.0 +Entfernung vom Start: 717 Minuten, Bewertung: 0.3 +Entfernung vom Start: 1076 Minuten, Bewertung: 3.8 +Entfernung vom Start: 1433 Minuten, Bewertung: 1.7 +hotels4.txt: +Entfernung vom Start: 340 Minuten, Bewertung: 4.6 +Entfernung vom Start: 676 Minuten, Bewertung: 4.6 +Entfernung vom Start: 1032 Minuten, Bewertung: 4.9 +Entfernung vom Start: 1316 Minuten, Bewertung: 4.9 +hotels5.txt: +Entfernung vom Start: 317 Minuten, Bewertung: 5.0 +Entfernung vom Start: 636 Minuten, Bewertung: 5.0 +Entfernung vom Start: 987 Minuten, Bewertung: 5.0 +Entfernung vom Start: 1286 Minuten, Bewertung: 5.0 +\end{minted} + +\section{Aufgabe 4: Würfelglück} +Es soll eine \enquote{Mensch ärgere dich nicht}-Simulation geschrieben werden. +Dabei auch werden modifizierte Würfel eingesetzt mit einer veränderten +Seitenanzahl und/oder veränderten Augenzahlen. Aus einer gegebenen +Würfelauswahl sollen alle möglichen Würfelpaare wiederholt gegeneinander +spielen. Dadurch soll bestimmt werden, wie gut jeder Würfel geeignet ist, um zu +gewinnen. + +\subsection{Lösungsidee} +Mithilfe eines Zufallsgenerators kann eine Seite des Würfels und somit die +Augenzahl bestimmt werden. Dann muss entschieden werden welche Figur gezogen +wird. In der folgenden Reihenfolge wird probiert, die Figuren zu ziehen: +\begin{enumerate} + \item Wenn eine Figur auf Feld A steht, muss diese gezogen werden. + \item Wurde eine 6 gewürfelt, wird eine neue Figur rausgebracht. + \item Die vorderste Figur auf der Laufbahn, die gezogen werden kann, wird gezogen. + \item Die Figur auf dem Zielfeld, die der Laufbahn am nächsten ist, wird gezogen. +\end{enumerate} +Welche Figur auf dem Zielfeld gezogen wird, ist nicht in den Regeln festgelegt. +Es wäre auch möglich so zu ziehen, dass möglichst keine Figur vor dem Ziel +blockiert wird, d. h. sie mit keiner möglichen Augenzahl gezogen werden kann. +Allerdings wird der Fall, dass zwei verschiedene Figuren auf die Zielfelder +gezogen werden können, selten auftreten. Außerdem wird durch Regeländerung +zu einer festen Priorität der Figuren auf der Laufbahn klar, dass so weit +vorausschauende Lösungen nicht in dieser Aufgabe erwartet werden. + +Sollte der Spieler eine 6 würfeln, ist er nochmal dran, ansonsten der andere. +Allerdings stellt sich die Frage, ob dieser auch nochmal dran ist, wenn er mit +der 6 nicht ziehen kann. Nach der Anmerkung gilt \enquote{Wenn kein Stein bewegt +werden kann, verfällt der Zug}. Hier ist fraglich, was mit \enquote{Zug} gemeint +ist. Einerseits können mehrere bewegte Figuren hintereinander als ein Zug +angesehen werden, da der gleiche Spieler dran ist. Allerdings werden zwei +Figuren gezogen, weshalb es auch als zwei Züge angesehen werden kann. Mit +\enquote{Wer eine \enquote{6} würfelt, hat nach seinem Zug einen weiteren Wurf +frei.} wird letzteres unterstützt, weil \enquote{nach seinem Zug} bedeutet, dass +bereits ein Zug beendet ist, bevor das zweite Mal gewürfelt wird. Es sind also +zwei Züge sind und nur der erste der beiden verfällt, sodass der Spieler auch +nach Verfallen eines Wurfes mit einer 6 nochmal würfeln darf. + +Es ist auch möglich, dass ein Spiel unentschieden ausgeht. Wenn beide Spieler z. B. +keine 1 auf dem Würfel haben, diese aber brauchen, um ins Ziel zu kommen. Stellt +sich die Frage, wie unentschiedene Spiele gezählt werden. Eine Möglichkeit wäre die +Spiele zu wiederholen. Allerdings steht in der Aufgabenstellung: \enquote{wie gut diese +Würfel geeignet sind, um das Spiel zu gewinnen}. D. h., dass es irrelavant ist, ob +ein Würfel verloren oder unentschieden gespielt hat, sondern nur, wie oft gewonnen +wurde. + +\subsection{Umsetzung} +Das Programm wird in Rust implementiert. Als Argumente müssen Dateipfade zu den +Eingabedateien übergeben werden. Das Spielfeld ist in einer Struktur gespeichert. +Die Laufbahn wird einem 40er-Array innerhalb der Struktur gespeichert. Die +Figuren der beiden Spieler werden durch 0 und 1 dargestellt. Auch die Spieler werden +Spieler 0 und Spieler 1 genannt. Ist an der entsprechenden Stelle keine +Figur, wird dies durch -1 dargestellt. Mit dem gleichen Format hat die Struktur zwei +Arrays für die Zielfelder für die beiden Spieler. Außerdem hat die Struktur ein Attribut, das +die Anzahl der Figuren auf den B-Feldern der beiden Spieler in einem Array speichert. +Schließlich hat die Struktur noch ein Attribut, das in einem 2d-Array die Positionen +der Figuren abgespeichert, der erste Index 0 oder 1 entsprechend der beiden Spieler und +der zweite Index für die Figuren des jeweiligen Spielers. Dies ist notwendig, damit schnell +die Figuren gezogen werden können, ohne das das ganze Laufbahn-Array durchsucht werden muss. +Die Methode \mintinline{rs}|step| der Struktur \mintinline{rs}|Field| zieht die Figuren unter Angabe einer +Figur und dem Würfelergebnis, wenn es möglich ist, und gibt je nach Erfolg +einen Boolean zurück. Außerdem ruft sie die Methode \mintinline{rs}|beat| auf, um die Figur zu +schlagen, die dem Zug im Weg stehen. + +In der \mintinline{rs}|main| Funktion wird mit einer for-Schleife über die Eingabedateien iteriert. +In einer weiteren for-Schleife werden alle möglichen Würfelkombinationen ermittelt. +Wie oft gespielt wird, kann durch die Variable \mintinline{rs}|runs| verändert werden. +Eine while-Schleife läuft, solange das Spiel läuft. Mit der \enquote{rand} +Bibliothek werden Zufallszahlen erstellt und die Augenzahl bestimmt. Dann wird +entsprechend des oben beschriebenen Algorithmus die Methode +\mintinline{rs}|step| aufgerufen. +Wurde erfolgreich ein Zug gemacht, wird überprüft, ob der Spieler gewonnen hat. +Konnte nicht gezogen werden, wird in einem Hashset die Augenzahl gespeichert. +Kann bei keiner Augenzahl bei beiden Spielern eine Figur bewegt werden, wird das Spiel +für Unentschieden erklärt. Wurden alle Spiele simuliert, wird das Ergebnis ausgegeben. + +\subsection{Beispiele} +Es folgen die Ergebnisse zweier durchgeführten Simulationsreihen. +\begin{minted}[breaklines]{text} +beispieldaten/wuerfel2.txt: +1. Würfel: Würfelseiten: [1, 6, 6, 6, 6, 6], Siege: 783855, Siegwahrscheinlichkeit: 0.97981875, Niederlagen: 16145, Niederlagewahrscheinlichkeit: 0.02018125, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +2. Würfel: Würfelseiten: [1, 1, 6, 6, 6, 6], Siege: 603829, Siegwahrscheinlichkeit: 0.75478625, Niederlagen: 196171, Niederlagewahrscheinlichkeit: 0.24521375, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +3. Würfel: Würfelseiten: [1, 1, 1, 6, 6, 6], Siege: 404042, Siegwahrscheinlichkeit: 0.5050525, Niederlagen: 395958, Niederlagewahrscheinlichkeit: 0.4949475, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +4. Würfel: Würfelseiten: [1, 1, 1, 1, 6, 6], Siege: 205234, Siegwahrscheinlichkeit: 0.2565425, Niederlagen: 594766, Niederlagewahrscheinlichkeit: 0.7434575, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +5. Würfel: Würfelseiten: [1, 1, 1, 1, 1, 6], Siege: 3040, Siegwahrscheinlichkeit: 0.0038, Niederlagen: 796960, Niederlagewahrscheinlichkeit: 0.9962, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +beispieldaten/wuerfel3.txt: +1. Würfel: Würfelseiten: [1, 2, 5, 6], Siege: 753656, Siegwahrscheinlichkeit: 0.753656, Niederlagen: 246344, Niederlagewahrscheinlichkeit: 0.246344, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +2. Würfel: Würfelseiten: [1, 2, 3, 4, 5, 6, 7, 8], Siege: 612055, Siegwahrscheinlichkeit: 0.612055, Niederlagen: 387945, Niederlagewahrscheinlichkeit: 0.387945, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +3. Würfel: Würfelseiten: [1, 2, 3, 4, 5, 6], Siege: 593636, Siegwahrscheinlichkeit: 0.593636, Niederlagen: +406364, Niederlagewahrscheinlichkeit: 0.406364, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +4. Würfel: Würfelseiten: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], Siege: 446248, Siegwahrscheinlichkeit: 0.446248, Niederlagen: 553752, Niederlagewahrscheinlichkeit: 0.553752, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +5. Würfel: Würfelseiten: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], Siege: 440136, Siegwahrscheinlichkeit: 0.440136, Niederlagen: 559864, Niederlagewahrscheinlichkeit: 0.559864, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +6. Würfel: Würfelseiten: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], Siege: 154269, Siegwahrscheinlichkeit: 0.154269, Niederlagen: 845731, Niederlagewahrscheinlichkeit: 0.845731, Unentschieden: 0, Unentschiedenwahrscheinlichkeit: 0 +\end{minted} +Allgemein sind die Würfel, mit der größten Wahrscheinlichkeit eine 6 zu würfeln, +am besten. Zum einen, da eine 6 benötigt wird, um rauszukommen. Außerdem kann man +nochmal würfeln, auch wenn man mit der 6 nicht ziehen konnte. Sehr große Augenzahlen +dagegen sind zwar gut, um viele Schritte auf einmal zu gehen. Allerdings sind sie +kurz vor dem Ziel von Nachteil, da man mit ihnen nicht mehr gehen kann. + +\part{Runde 2} +Zum Anlass des Jubiläums (40. Wettbewerb) gibt es diesmal eine Bonusaufgabe +zusätzlich zu den normalen drei Aufgaben. Aus diesen vier Aufgaben müssen zwei +ausgewählt werden. Der Quelltext meiner Programme, die Aufgabenstellung und +weitere Beispielausgaben findet man in meinem +\href{https://git.zinkel.org/MrGeorgen/bwinf40-runde2}{Git-Repository}. + +\section{Aufgabe 2: Rechenrätsel} +Das Programm soll Rechenrätsel erstellen, bei denen die Operanden aus einer +einzigen Ziffer bestehen. Das Ergebnis des Terms muss positiv sein. Es soll nur +eine Möglichkeit geben, die Grundrechenarten als Operatoren zwischen die +Operanden einzusetzen, sodass die Gleichung erfüllt ist. Ein Beispiel für so ein +Rätsel ist: $4\cdot4\cdot3=13$ + +\subsection{Lösungsidee} +\subsubsection{Allgemeines} +Der Beweis, ob ein Rechenrätsel eindeutig lösbar ist, ist ein +Entscheidungsproblem mit NP-Schwere. Eine mögliche Lösung ist, eine interessante +Ziffernfolge zufällig zu wählen und alle möglichen Kombinationen von Operatoren +auszuprobieren. Kommt ein Ergebnis nur einmal vor, wurde ein eindeutig lösbares +Rätsel gefunden, was in der Regel der Fall sein sollte, ansonsten muss der +Vorgang mit anderen Ziffern wiederholt werden. + +Theoretisch wäre es möglich, dass die Operanden immer +zufällig so gewählt werden, dass es kein interessantes und eindeutiges Rätsel +gibt und das Programm somit nie zu einem Ergebnis kommt. Allerdings läuft die +Wahrscheinlichkeit dafür gegen null. + +Die beschriebene Brute-Force\footnote{Alle Möglichkeiten werden ausprobiert.} +Operation dauert allerdings bei zunehmender Anzahl der Operatoren exponentiell länger. Die Anzahl +der Möglichkeiten $|\Omega|$ kann in Abhängigkeit von der Operatorenanzahl $n$ +berechnet werden: $|\Omega|=4^{n}$. Dabei gilt es zu beachten, dass dies der +Maximalwert ist, da die Division oft im Vorhinein ausgeschlossen werden kann, +weil diese eine nicht ganze Zahl ergibt. Das Programm sollte Rätsel mit mindestens 15 +Operatoren erstellen können, da dies in der Aufgabenstellung als Richtwert +angegeben wird. Für $n=15$ gilt $|\Omega|=4^{15}=1073741824\approx10^{6}$. So +viele Möglichkeiten können noch mit einer guten Laufzeit berechnet werden. + +\subsubsection{Die Null als Operand?} + +Nach Aufgabenstellung ist jeder Operand nur eine Ziffer. Bei den gegebenen +Beispielen kommt jede Ziffer vor außer der Null, allerdings wird auch nicht +ausdrücklich gesagt, dass man sie nicht verwenden darf. Die Addition und +Subtraktion einer Null kann in einem Rätsel nicht verwendet werden, da beide +Operationen das gleiche Ergebnis haben und das Rätsel damit uneindeutig wäre. Da +die Division durch Null nicht definiert ist, bleibt nur noch die Multiplikation +übrig. Es ergibt jedoch wenig Sinn ein Rätsel zu stellen, wo bereits zu Beginn +ein Teil der Lösung bekannt ist. Dies gilt insbesondere, da die Rätsel nach +Aufgabenstellung \enquote{richtig schwer} sein sollen. Also wird die Null in +meiner Lösung nicht als Operand benutzt. + +\subsubsection{Interessante Rätsel} +Die Aufgabenstellung gibt nicht genau vor, wie ein Rätsel, das \enquote{interessant und +unterschiedlich} ist, zu sein hat. Allerdings wird ein Beispiel gegeben, wie ein +Rätsel aussehen kann. Das Beispielrätsel habe ich mit einer leicht modifizierten +Version meines Programms lösen lassen: +\begin{minted}[breaklines]{text} +Rätsel: 4 ○ 3 ○ 2 ○ 6 ○ 3 ○ 9 ○ 7 ○ 8 ○ 2 ○ 9 ○ 4 ○ 4 ○ 6 ○ 4 ○ 4 ○ 5 = 4792 +Lösung: 4 * 3 * 2 * 6 * 3 * 9 + 7 * 8 : 2 * 9 * 4 - 4 * 6 - 4 * 4 * 5 = 4792 +\end{minted} +Es fällt auf das jeder Operator mindestens einmal vorkommt, wobei die +Multiplikation überwiegt. Außerdem ist das Ergebnis noch relativ klein im +Vergleich zu anderen eindeutig lösbaren Rätseln, wie ich beim Vergleich mit der +ungefilterten Ausgabe meines Programms festgestellt habe. Zudem kommen einige +verschiedene Ziffern vor. Meine Regeln für ein interessantes Rätsel sind noch +etwas strenger, da in dem Beispiel schon recht oft die Multiplikation vertreten +ist. Diese lauten wie folgt: +\begin{enumerate} + \item Sei $n$ die Anzahl der Operatoren und $m_i$, die Anzahl, mit der + der jeweilige Operator vorkommt. Dann soll gelten: + \begin{align} + \frac{n}{10} - 1 < m_i \leq \frac{n}{2} + 1 + \end{align} + Die Division ist oft nicht möglich, da das Ergebnis keine ganze + Zahl ist. Deshalb muss die Untergrenze relativ klein sein, damit + für die Division die Bedingung noch erfüllt werden kann. Die + Formel wurde so gewählt, dass bei Rätsel mit zwei Operatoren + noch zweimal derselbe Operator gewählt werden darf. Allerdings + finde ich es bei Rätseln mit drei Operatoren nicht interessant, + jedes Mal denselben Operator zu haben. Darum habe ich die Formel + so gewählt, dass in diesem Fall die Obergrenze nicht erfüllt + ist. + \item Sei $n$ die Anzahl der Ziffern und $m_i$, die Anzahl mit der die + jeweilige Ziffer vorkommt. Dann soll gelten: + \begin{align} + \frac{n}{18} - 1 < m_i \leq \frac{2}{9} \cdot n + 1 + \end{align} + Für diese Untergrenze habe ich mich entschieden, damit, wenn + durchschnittlich jede Ziffer der neun Ziffern zweimal vorkommen sollte, + mindestens eine vorhanden ist. Aus einem ähnlichen Grund habe + ich auch die Obergrenze so gewählt, dass $\lim_{n \to \infty} + \frac{2}{9} \cdot n + 1 = \lim_{n \to \infty} \frac{2}{9} \cdot n$ +\end{enumerate} +In der Regel werden viele interessante Rätsel gefunden. Da das Beispielrätsel +ein kleines Ergebnis hat und kleinere Ergebnisse auch eleganter sind, wird +von allen interessanten Rätseln, das mit dem kleinsten Ergebnis ausgewählt. + +\subsection{Umsetzung} +\subsubsection{Umgebung und Bibliotheken} +Die Lösungsidee wird in Rust nightly implementiert, da Trait Aliase noch nicht +in Rust stable sind. Jeder Typ, der ein Trait implementiert, muss bestimmte +Methoden haben. Traits sind daher für Generics essenziell. Trait Aliase machen es +nun möglich für mehrere Traits einen Alias zu erstellen, was Schreibarbeit +spart. Außerdem werden einige crates, also Abhängigkeiten in Rust, verwendet: +\begin{itemize} + \item Mit rand werden zufällige Ziffern als Operanden erstellt. + \item Mit clap werden die Kommandozeilenargumente verarbeitet. + \item Zudem werden noch einige crates des rust-num Teams genutzt. + Dabei wird nicht das meta-crate num verwendet, sondern die + sub-crates werden einzeln verwendet, um die Abhängigkeiten + kleinzuhalten. num-traits ist dabei u. a. für die Konvertierung + eines bestimmten Integer-Typs zu einen generischen Integer-Typ + zuständig. num-derive stellt Macros zur Verfügung, die es + erlauben Integers zu Enums zu konvertieren. Mit num-integer + können arithmetischen Operationen generisch für verschiedene + Integer-Typen implementiert werden. +\end{itemize} + +\subsubsection{Benutzung} +Mit der Option -c kann die Anzahl der Operatoren angegeben werden. Der +Standartwert ist 5. Allerdings können maximal Rätsel mit 32 Operatoren berechnet +werden, da die verwendeten Operatoren eines Rätsels in 64 Bit Integers +gespeichert werden. Da es vier Grundrechenarten gibt brauchen wir für jeden +Operator 2 Bits, um diesen darzustellen. Da $64/2=32$ können also maximal 32 +Operatoren verwendet werden. Mit dem flag -s kann man die Ausgabe der Lösung zu dem +Rätsel einschalten. Schließlich können optional mit -d die Operanden angegeben werden, z. +B. : \mintinline{text}|-d={1,4,5,8}|. Dies ist sehr nützlich zu Testzwecken. Wenn man die +Operanden nicht angibt, werden automatisch interessante gewählt. + +\subsubsection{Implementierungsart} + +In der main-Funktion befindet sich direkt eine while-Schleife, die solange läuft +bis ein interessantes Rätsel gefunden wurde. In der Regel wird die Schleife nur einmal +durchlaufen, weil sich für die erste ausgewählte Operandenkombination normalerweise +auch ein interessantes und eindeutiges Rätsel ergibt. + +Wenn der Nutzer keine +Operanden angibt, werden diese zufällig ausgewählt. Dabei befinden sich die +Ziffern in einem Vector\footnote{Implementierung von Arrays mit +dynamischer Größe aus Rusts Standartbibliothek} und werden daraus +zufällig gewählt. Damit die minimale und maximale Anzahl aller Ziffern +garantiert werden kann, sodass das Rätsel interessant ist, werden in Laufe der +Auswahl Ziffern aus dem Vector entfernt. + +Danach wird die Funktion \mintinline{rs}|calc_results| mit diesen Operanden +aufgerufen. Diese kann durch Generics alle Integertypen für die +(Zwischen)ergebnisse verwenden. Das größte Ergebnis für die Operanden wird +berechnet, indem sie alle miteinander multipliziert werden. Wenn dieses zu groß +ist für 64 Bit Integers, werden 128 Bit Integers verwendet, ansonsten 64 Bit +Integers. Dafür habe ich mich entschieden, da mit 64 Bit Rechnern schneller mit +64 Bit Integers gerechnet werden kann. Jedoch kann man trotzdem Rätsel berechnen, +die 128 Bit Integers erfordern. + +Innerhalb der Funktion \mintinline{rs}|calc_results| gibt es eine for-Schleife, +die über alle Möglichkeiten, Operanden mit Punkt- bzw. Strichrechnung zu wählen, +iteriert. Dabei wird die Variablen \mintinline{rs}|dm_as_map| nach jedem +Durchlauf um eins erhöht. Jedes Bit der Variable steht dabei für Punkt- oder +Strichrechnung, wobei das least significant Bit für den Operatoren ganz links +steht. + +In der Schleife wird die Funktion \mintinline{rs}|multiplicate_divide| mit einer Sequenz von +Operanden, zwischen denen nur Punktrechnung vorkommt, aufgerufen. Diese ruft +sich selber doppelt rekursiv auf, wobei einmal multipliziert und einmal +dividiert wird. Die Division wird ausgelassen, wenn das Zwischenergebnis keine +ganze Zahl ist. Wenn das Ende der Sequenz erreicht ist, werden die verwendeten +Operatoren in einer Hashmap mit dem Zwischenergebnis als Schüssel abgespeichert. +Wenn das Zwischenergebnis bereits einmal vorkam, wird unter dem Ergebnis +\mintinline{rs}|None| abspeichert und das Zwischenergebnis somit als uneindeutig +markiert. Die Hashmaps mit den möglichen Zwischenergebnissen werden zusammen in +einem Vector \mintinline{rs}|results_multiplicate| gespeichert. Zusätzlich +werden die Operanden, die nicht Teil einer Punktrechnungssequenz sind, einfach +in den Vector übernommen, indem eine Hashmap mit nur einem möglichen +Zwischenergebnis erstellt wird. + +In der rekursiven Funktion \mintinline{rs}|add_sub| wird mit einer for-Schleife +über alle möglichen Zwischenergebnisse iteriert. Innerhalb der Schleife ruft +sich die Funktion pro Durchlauf zweimal auf, wobei einmal addiert und einmal +subtrahiert wird. Dies passiert solange, bis die letzte Hashmap mit möglichen +Ergebnissen erreicht wurde. Dann wird das Ergebnis mit den verwendeten +Operatoren mithilfe der Struktur \mintinline{rs}|ResultStore| gespeichert, wobei +es auch passieren kann, dass das Ergebnis direkt als uneindeutig markiert wird, da +schon eines der Zwischenergebnisse uneindeutig war. + +Die Struktur \mintinline{rs}|ResultStore| besteht aus einer Hashmap mit den +Operatoren als Wert und den dazugehörigen Ergebnissen als Schüssel. Außerdem hat +sie noch ein weiters Attribut, ein Hashset, mit den Ergebnissen für die das +Rätsel uneindeutig ist. Die Methode \mintinline{rs}|store| hat als Parameter +das Ergebnis und eine Option von Operatoren. Wenn die Option +\mintinline{rs}|None| ist, wird das Ergebnis in das Hashset der uneindeutigen +Ergebnisse aufgenommen. Wenn das Ergebnis weder in der Hashmap noch in dem +Hashset ist, wird das Rätsel in der Hashmap gespeichert. Wenn es bereits in der +Hashmap ist, wird es als uneindeutig im Hashset gespeichert. + +Schließlich wird über alle Schlüssel-Werte-Paare der Hashmap einer Instanz der +\mintinline{rs}|ResultStore| Struktur iteriert. Es wird gezählt wie oft welche +Operatoren verwendet wurden und überprüft, ob die Anzahl innerhalb des Bereiches +liegt, in dem das Rätsel als interessant befunden wird. Von allen interessanten +und uneindeutigen Rätseln wird bestimmt, welches das kleinste Ergebnis hat, und +dieses wird ausgegeben. Wenn kein interessantes Rätsel gefunden wurde, muss die +while-Schleife der main-Funktion noch einmal durchlaufen werden. + +\subsection{Beispiele} +\rechen{2} +\rechen{3} +\rechen{6} +\rechen{15} +\rechen{26} + +\section{Aufgabe 3: HexMax} +Mit Stäbchen wird eine hexadezimale Zahl dargestellt wie bei einer +Siebensegmentanzeige. Ein Programm soll geschrieben werden, dass bei einer +gegebenen Maximalzahl für die Umlegungen, die Stäbchen so umgelegt werden, +dass die Zahl möglichst groß wird. + +\subsection{Lösungsidee} +Durch ebenso häufiges Hinlegen und Wegnehmen der Stäbchen erhält man eine Hex-Zahl, +die man auch durch Umlegen der Stäbchen erreichen kann. Der Algorithmus kann also erstmal mehr +Stäbchen hinlegen wie wegnehmen oder umgekehrt, solange am Ende die Anzahl der +hingelegten Stäbchen gleich der der weggenommenen Stäbchen ist. + +Die größte Zahl erhält man, wenn man möglichst die vorderen Ziffern erhöht. Der +Algorithmus geht, angefangen mit der ersten Ziffer, jede Ziffer der Zahl durch. +Davon ausgenommen sind Ziffern, die schon geändert wurden. Bei jeder Ziffer, +sofern sie nicht schon F ist, wird probiert sie auf F,E,... umzulegen. Dabei +wird gezählt, +wie viele Stäbchen weggenommenen und hingelegt werden müssen. Überschreitet +weder die Gesamtzahl der weggenommenen Stäbchen, noch die der hingelegten Stäbchen +die gegebene maximale Anzahl der Umlegungen, wird die Ziffer entsprechend +geändert. Ansonsten wird probiert die Ziffer zu der nächst kleineren Ziffern zu +ändern. Dies wird so lange wiederholt, bis die Änderung der Ziffer erfolgreich +war oder bereits alle Ziffern, die größer sind, ausprobiert wurden. Danach wird +zu der nächsten Ziffer übergegangen. + +Nachdem die vorderen Ziffern entsprechend erhöht wurden, kann es sein, dass noch +Stäbchen benötigt werden oder loszuwerden sind, damit gleich viele Stäbchen +weggenommenen wie hingelegt wurden. Da zuvor schon die Ziffern nach Möglichkeit +erhöht wurden, kann dies nur erreicht werden, indem Ziffern verringert werden. +Damit das nicht so stark ins Gewicht fällt, wird über die Ziffern von hinten +iteriert. Ziffer für Ziffer wird nun probiert, die Ziffer so zu ändern, dass sich +der Abstand zwischen der Anzahl der hingelegten und der weggenommenen Stäbchen +verringert. Es kann auch eine vorherige Änderung der Ziffer wieder rückgängig +gemacht +werden. Es kommt zu einem Fehler, wenn man bei einer hinteren Ziffer weniger +Stäbchen hätte loswerden bzw. wegnehmen sollen, um später bei einer Ziffer, die +weiter vorne ist, mehr Stäbchen hinlegen bzw. wegnehmen zu können. Wenn sie +dadurch größer würde oder es dadurch überhaupt erst möglich wäre den +Ausgleichsvorgang schon bei ihr abzuschließen, ist das Ergebnis falsch. +Glücklicherweise tritt dieser Fall bei den gegebenen Beispielen nicht ein. Unter +\ref{negativ} findet sich noch ein entsprechendes Negativbeispiel. Auch bei +diesem Algorithmusteil darf natürlich nicht die maximale Anzahl der Umlegungen +überschritten werden. Der Algorithmus wird so lange wiederholt bis die maximale +Anzahl der Umlegungen ausgeschöpft wurde. + +Schließlich werden Paare von jeweils einem hingelegten und einem weggenommenen +Stäbchen gebildet, die eine Umlegung ergeben. Theoretisch könnte es passieren, +dass dabei \enquote{die Darstellung einer Ziffer komplett \enquote{geleert}} +wird. Praktisch tritt dieser Fall bei den gegebenen Beispielen jedoch nicht ein. + +Sei $n$ die Anzahl der Ziffern, so hat der Algorithmus im besten Fall eine +Laufzeit von $O(n) = n$, wenn keine Wiederholung notwendig ist. Dies ist für die +Beispiele 2-5 auch die tatsächliche Laufzeit. Da es maximal $n$ Wiederholung +geben kann, ist die Laufzeit im schlechtesten Fall $O(n) = n^2$ + +\subsection{Umsetzung} +\subsubsection{Umgebung und Bibliotheken} +Die Lösungsidee wird in Rust implementiert. Zum Erleichtern der Implementierung +habe ich noch zwei crates\footnote{Abhängigkeiten in Rust} benutzt: +\begin{enumerate} + \item Mit clap werden die Kommandozeilenargumente verarbeitet. + \item derive\_more bietet u. a. die Möglichkeit die einzelnen Attribute + einer Struktur jeweils zu addieren oder zu subtrahieren wie bei + einem Vektor. +\end{enumerate} + +\subsubsection{Benutzung} +Dem Programm muss als Argument der Dateipfad zu der Eingabedatei übergeben +werden. Außerdem können noch weitere Optionen angepasst werden. Mit der Option +-d kann die Anzahl der Ziffern, die beim Ausgeben des Zwischenstands der +Umlegungen maximal in einer Zeile anzeigt werden sollen, geändert werden. +Standardmäßig sind dies 20 Ziffern, da jede Ziffer ein +ASCII-Art\footnote{ASCII-Art ist die Darstellung eines Bildes oder Piktogramms +mit Zeichen der ASCII-Codierung.} ist und +dementsprechend Platz braucht. Beim Verwenden des flags -n wird die Anzahl +der durchgeführten Umlegungen ausgegeben. Benutzt man den flag -s, so wird der +Zwischenstand der 7-Segmentanzeige nach jeder Umlegung ausgegeben. Schließlich +kann man den flag -l verwenden, für den Latex-Code mit dem Ergebnis des +Programms. Dies ist hilfreich, da dafür gesorgt wird, dass kein Seitenumbruch +mitten in einem ASCII-Art auftritt. + +\subsubsection{Implementierungsart} +Die Implementierung ist in zwei Dateien aufgeteilt. In der Datei lib.rs wird die +größtmögliche Hex-Zahl ermittelt und die Umlegungen werden entsprechend +generiert. In der Datei main.rs werden die Nutzereingaben verarbeitet und das +Ergebnis sowie weitere Informationen ausgegeben. + +Kommen wir zum Code in der Datei main.rs: Zuerst werden in der +main-Funktion die Kommandozeilenargumente verarbeitet. Danach wird die Funktion +\mintinline{rs}|hexmax::largest_hex| aufgerufen. Diese ermittelt die +größtmögliche Hex-Zahl mit der gegebenenen maximalen Anzahl der Umlegungen. +Danach wird die Hex-Zahl mit der Funktion \mintinline{rs}|hexmax::to_hex_str| zu +einem String konvertiert und ausgegeben. Falls die Zwischenstände der Umlegungen +ausgegeben werden sollen, wird die Funktion \mintinline{rs}|hexmax::gen_swaps| +aufgerufen, die die Umlegungen zurückgibt. Anschließend wird jede Umlegung +hintereinander ausgeführt und die 7-Segmentanzeige nach jeder Umlegung mithilfe +der Funktion \mintinline{rs}|swap_print| als ASCII-Art ausgegeben. + +In der Datei lib.rs gibt ein konstantes Array namens \mintinline{rs}|HEX|, dass +am Index der jeweiligen Ziffer eine Bitmap der 7-Segmentrepräsentation enthält. +Bei 1 leuchtet das Segment und bei 0 nicht. + +Die Funktion \mintinline{rs}|change_digits| durchläuft Ziffer für Ziffer eines +als Argument übergebenen Iterators. Für jede Ziffer wird nun jede mögliche +Änderung der Ziffer durchgegangen, angefangen mit F,E,... . Dabei wird nach +folgender Vorgehensweise berechnet, wie viele Stäbchen im Vergleich zur +ursprünglichen Ziffer hingelegt und weggenommenen werden müssen: + +Sei $u$ die 7-Segmentbitmap der ursprünglichen Ziffer und $n$ die der neuen +Ziffer: +\begin{align} + c = u \oplus n \\ + h = c \& n \label{h} \\ + g = c \& u \label{g} +\end{align} +Mit diesen Formeln erhält man eine Bitmap der hingelegten Stäbchen $h$ und der +weggenommenen Stäbchen $g$. Zählt man nun die Einsen der binären Schreibweise +von $h$ oder $g$, so ergibt sich die Anzahl der hingelegten bzw. weggenommenen +Stäbchen. Mit diesen sowie der Gesamtzahl der hingelegten bzw. weggenommenen +Stäbchen wird eine der Funktion \mintinline{rs}|change_digits| übergebene +Lambdafunktion aufgerufen, sofern bei der Ziffernänderung nicht die maximale Anzahl der +Umlegungen überschritten werden würde. Diese entscheidet mit diesen Angaben, ob die Ziffer +entsprechend geändert werden soll. Diese Änderung ist entweder final oder es +wird probiert, die Ziffer auf einen niedrigen Wert zu ändern. + +In der Funktion \mintinline{rs}|largest_hex| wird zuerst die Eingabedatei +ausgelesen. Im Vector \mintinline{rs}|digits| werden die einzelnen Ziffern der +gegebenen Hex-Zahl gespeichert. Innerhalb einer for-Schleife wird der +Algorithmus wiederholt, bis die Anzahl der maximalen Umlegungen ausgeschöpft ist +oder er schon so oft wiederholt wurde wie die Anzahl der Ziffern. Letzteres +bedeutet nämlich, dass keine größere Zahl mehr gefunden werden kann, die alle +Umlegungen ausschöpft. Zuerst wird probiert die vorderen Ziffern zu erhöhen. +Dafür wird die Funktion \mintinline{rs}|change_digits| mit einer Lambdafunktion +aufgerufen, die gleich die erste Ziffernänderung final akzeptiert. Die erste ist +dabei automatisch die größtmögliche Erhöhung. + +Wenn die Anzahl der hingelegten ungleich der weggenommenen Stäbchen ist, müssen +noch Stäbchen genommen bzw. hingelegt werden. Dafür wird Ziffer für Ziffer von +hinten mithilfe der Funktion \mintinline{rs}|change_digits| betrachtet. Jede +Ziffer wird nun so geändert, dass der Abstand zwischen den hingelegten und +weggenommenen Stäbchen am geringsten ist. Nachdem alle Ziffern durchgegangen +worden sind, ist die Anzahl der hingelegten gleich der weggenommenen Stäbchen. +Allerdings kann es sein, dass dafür eine vorherige Änderung rückgängig gemacht +werden musste und die maximale Anzahl der Umlegungen nicht ausgeschöpft wurde. +In diesem Fall wird der Algorithmus nochmal wiederholt. + +Die Funktion \mintinline{rs}|gen_swaps| generiert aus den Änderungen der Ziffern +konkrete Umlegungen. Dafür wird in einer for-Schleife über jede Ziffernänderung +iteriert. Wie in \eqref{h} und \eqref{g} beschrieben, wird ermittelt welche +Stäbchen dafür hingelegt und weggenommenen werden müssen. Die Bitmaps $h$ und +$g$ werden Bit für Bit durchlaufen. Für jede 1, d. h. das Stäbchen wurde +geändert, wird diese Änderung dem Vector \mintinline{rs}|moves_put| angehängt, +wenn das Stäbchen hingelegt wurde. Wenn es weggenommenen wurde, dann wird sie +dem Vector \mintinline{rs}|moves_taken| angehängt. Die beiden +Vectors\footnote{Da Vector der Name der Struktur ist, habe ich hier auch die +englische Mehrzahl verwendet. Eigentlich müsste es heißen \enquote{Variablen der +Struktur Vector}} werden schließlich zurückgegeben. Ordnet man die Elemente der +Vectors mit dem gleichen Index einander zu, so erhält man die Umlegungen. + +Die Funktion \mintinline{rs}|to_hex_str| konvertiert eine Slice mit den Ziffern +zu einem hexadezimalen String. Dies wird erreicht, indem mit einer for-Schleife +über alle Ziffern iteriert wird, und jede Ziffer einzeln zu dem entsprechenden +Zeichen konvertiert wird. + +\subsection{Beispiele} +\subsubsection{gegebene Beispiele} +\beispiel{0} +Die Zahl D24 wird erst mit 3 weggenommenen und 2 hingelegten Stäbchen auf F24 +erhöht. Danach kann man allerdings das übrige Stäbchen nicht bei den Ziffern 4 +oder 2 loswerden, sodass die Zahl auf E24 geändert wird, da dort gleich viele +Stäbchen hingelegt wie weggenommenen wurden. Darauffolgend wird wieder probiert +die Ziffern zu erhöhen, wobei die erste Ziffer ausgelassen wird, sodass der +Algorithmus schließlich zur Lösung EE4 kommt. + +\newcounter{beispielcounter} +\forloop{beispielcounter}{1}{\value{beispielcounter}<5}{ + \beispiel{\arabic{beispielcounter}} +} + +\subsubsection{Negativbeispiel\label{negativ}} +\begin{minted}[]{text} +$ hexmax testdaten/negativBeispiel.txt +ED8 +\end{minted} + +Die richtige Lösung wäre F89. + +\end{document}