258 lines
12 KiB
TeX
258 lines
12 KiB
TeX
\documentclass[a4paper,10pt,ngerman]{scrartcl}
|
|
\usepackage{babel}
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage[a4paper,margin=2.5cm,footskip=0.5cm]{geometry}
|
|
|
|
% Die nächsten drei Felder bitte anpassen:
|
|
\newcommand{\Aufgabe}{Aufgabe 3: HexMax} % Aufgabennummer und Aufgabennamen angeben
|
|
\newcommand{\TeilnahmeId}{60813} % Teilnahme-ID angeben
|
|
\newcommand{\Name}{Marcel Zinkel} % Name des Bearbeiter / der Bearbeiterin dieser Aufgabe angeben
|
|
|
|
|
|
% Kopf- und Fußzeilen
|
|
\usepackage{scrlayer-scrpage, lastpage}
|
|
\setkomafont{pageheadfoot}{\large\textrm}
|
|
\lohead{\Aufgabe}
|
|
\rohead{Teilnahme-ID: \TeilnahmeId}
|
|
\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}
|
|
|
|
% Diese beiden Pakete müssen zuletzt geladen werden
|
|
%\usepackage{hyperref} % Anklickbare Links im Dokument
|
|
\usepackage{cleveref}
|
|
|
|
\usepackage{forloop}
|
|
|
|
\newcommand{\beispiel}[1] {
|
|
hexmax#1.txt:
|
|
\input{|target/debug/hexmax -s -l -d 17 beispieldaten/hexmax#1.txt}
|
|
}
|
|
|
|
% Daten für die Titelseite
|
|
\title{\textbf{\Huge\Aufgabe}}
|
|
\author{\LARGE Teilnahme-ID: \LARGE \TeilnahmeId \\\\
|
|
\LARGE Bearbeiter/-in dieser Aufgabe: \\
|
|
\LARGE \Name\\\\}
|
|
\date{\LARGE\today}
|
|
|
|
\begin{document}
|
|
|
|
\maketitle
|
|
\tableofcontents
|
|
|
|
\vspace{0.5cm}
|
|
|
|
\section{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$
|
|
|
|
\section{Umsetzung}
|
|
\subsection{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}
|
|
|
|
\subsection{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.
|
|
|
|
\subsection{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.
|
|
|
|
\section{Beispiele}
|
|
\subsection{gegebenene 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.
|
|
|
|
\beispiel{1}
|
|
\beispiel{2}
|
|
\newcounter{beispielcounter}
|
|
\forloop{beispielcounter}{3}{\value{beispielcounter}<6}{
|
|
hexmax\arabic{beispielcounter}.txt:
|
|
\inputminted[breaklines,breakanywhere]{text}{ergebnisdateien/\arabic{beispielcounter}.txt}
|
|
}
|
|
|
|
\subsection{Negativbeispiel\label{negativ}}
|
|
\begin{minted}[]{text}
|
|
$ hexmax testdaten/negativBeispiel.txt
|
|
ED8
|
|
\end{minted}
|
|
|
|
Die richtige Lösung wäre F89.
|
|
|
|
\section{Quellcode}
|
|
src/lib.rs:
|
|
\inputminted[breaklines,fontsize=\footnotesize]{rs}{src/lib.rs}
|
|
|
|
\end{document}
|