final commit
This commit is contained in:
@ -45,6 +45,13 @@
|
||||
%\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 \\\\
|
||||
@ -60,48 +67,191 @@
|
||||
\vspace{0.5cm}
|
||||
|
||||
\section{Lösungsidee}
|
||||
Durch ebenso oftes Hinlegen und Wegnehmen der Stäbchen erhält man eine Hex-Zahl,
|
||||
die man auch mit Umlegen erreichen kann. Der Algorithmus kann also erstmal mehr
|
||||
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 weggenommenen Stäbchen ist.
|
||||
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.
|
||||
Ausgenommen sind davon Ziffern, die schon geändert wurden.
|
||||
Jede Ziffer wird, sofern sie nicht schon F ist, probiert 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 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.
|
||||
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 Betrag der Differenz von der
|
||||
Anzahl der hingelegten und der weggenommenen Stäbchen zu verringert. Es kann
|
||||
auch eine vorherige Änderung der Ziffer noch geändert werden. Dabei darf
|
||||
natürlich nicht die maximale Anzahl der Umlegungen überschritten werden.
|
||||
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.
|
||||
|
||||
Der Algorithmus wird so lange wiederholt bis die maximale Anzahl der Umlegungen
|
||||
ausgeschöpft wurde. Dazu einmal das Beispiel aus der Aufgabenstellung: Die Zahl
|
||||
D24 wird erst mit 3 weggenommenen und 2 hingelegten Stäbchen auf F24 erhöht.
|
||||
Danach kann man allerdings das eine 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.
|
||||
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$
|
||||
|
||||
8D5 F89
|
||||
\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}
|
||||
Unwichtige Teile des Programms sollen hier nicht abgedruckt werden. Dieser Teil sollte nicht mehr als 2–3 Seiten umfassen, maximal 10.
|
||||
src/lib.rs:
|
||||
\inputminted[breaklines,fontsize=\footnotesize]{rs}{src/lib.rs}
|
||||
|
||||
\end{document}
|
||||
|
||||
1
Aufgabe3-HexMax/ergebnisdateien/0.txt
Normal file
1
Aufgabe3-HexMax/ergebnisdateien/0.txt
Normal file
@ -0,0 +1 @@
|
||||
größte Hex-Zahl: EE4
|
||||
1
Aufgabe3-HexMax/ergebnisdateien/1.txt
Normal file
1
Aufgabe3-HexMax/ergebnisdateien/1.txt
Normal file
@ -0,0 +1 @@
|
||||
größte Hex-Zahl: FFFEA97B55
|
||||
1
Aufgabe3-HexMax/ergebnisdateien/2.txt
Normal file
1
Aufgabe3-HexMax/ergebnisdateien/2.txt
Normal file
@ -0,0 +1 @@
|
||||
größte Hex-Zahl: FFFFFFFFFFFFFFFFD9A9BEAEE8EDA8BDA989D9F8
|
||||
1
Aufgabe3-HexMax/ergebnisdateien/3.txt
Normal file
1
Aufgabe3-HexMax/ergebnisdateien/3.txt
Normal file
@ -0,0 +1 @@
|
||||
größte Hex-Zahl: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA98BB8B9DFAFEAE888DD888AD8BA8EA8888
|
||||
1
Aufgabe3-HexMax/ergebnisdateien/4.txt
Normal file
1
Aufgabe3-HexMax/ergebnisdateien/4.txt
Normal file
@ -0,0 +1 @@
|
||||
größte Hex-Zahl: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB8DE88BAA8ADD888898E9BA88AD98988F898AB7AF7BDA8A61BA7D4AD8F888
|
||||
1
Aufgabe3-HexMax/ergebnisdateien/5.txt
Normal file
1
Aufgabe3-HexMax/ergebnisdateien/5.txt
Normal file
@ -0,0 +1 @@
|
||||
größte Hex-Zahl: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88EFA9EBE89EFA99FBDAA8E8EAD88AB899F8E8F9AA9E9AD88988EDA9A99888EDAD989A8BAFD8A888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
|
||||
1
Aufgabe3-HexMax/ergebnisdateien/negativBeispiel.txt
Normal file
1
Aufgabe3-HexMax/ergebnisdateien/negativBeispiel.txt
Normal file
@ -0,0 +1 @@
|
||||
größte Hex-Zahl: ED8
|
||||
@ -1,32 +1,51 @@
|
||||
use std::fs;
|
||||
|
||||
// Struktur für eine veränderte Ziffer
|
||||
#[derive(Clone, Copy)]
|
||||
struct Move {
|
||||
digit: u8,
|
||||
swaps: PutTaken,
|
||||
}
|
||||
|
||||
// enthält den ursprünglichen Wert der Ziffer und den aktuellen
|
||||
pub struct DigitHistory {
|
||||
pub original_digit: u8,
|
||||
move_option: Option<Move>,
|
||||
}
|
||||
|
||||
// zur Angabe welche Stäbchen verändert wurden
|
||||
pub struct ChangedSticks {
|
||||
pub digit_i: usize,
|
||||
pub stick_map: u8,
|
||||
}
|
||||
|
||||
/* vektorartige Struktur, die die Anzahl der hingelegten und weggenommenen Stäbchen enthält.
|
||||
* Rechenoperationen können, wie bei Vektoren durchgeführt werden. */
|
||||
#[derive(Clone, Copy, derive_more::SubAssign, derive_more::AddAssign, derive_more::Add)]
|
||||
struct PutTaken {
|
||||
put: u16,
|
||||
taken: u16,
|
||||
}
|
||||
|
||||
/* Rückgabetyp der Lambdafunktion, die ein Argument von change_digits ist. Damit kann das weitere
|
||||
* Vorgehen der Funktion gesteuert werden. */
|
||||
struct ChangeDigitsControl {
|
||||
change_digit: bool,
|
||||
|
||||
/* Bei true wird aus der inneren for-Schleife von change_digits ausgebrochen, sodass Änderungen
|
||||
* auf kleinere Ziffer nicht mehr ausprobiert werden. */
|
||||
break_free: bool,
|
||||
}
|
||||
|
||||
/* Bitmaps der 7-Segmentrepräsentation der Hex-Zahlen. Bei 1 ist ein Stäbchen da und bei 0 nicht. Es
|
||||
* folgt eine Auflistung, welches Bit für welches Stäbchen steht.
|
||||
* 2. höchstwertiges Bit: Stäbchen oben in der Mitte
|
||||
* 3. höchstwertiges Bit: Stäbchen oben links
|
||||
* 4. höchstwertiges Bit: Stäbchen oben rechts
|
||||
* 5. höchstwertiges Bit: zentrales Stäbchen
|
||||
* 6. höchstwertiges Bit: Stäbchen unten links
|
||||
* 7. höchstwertiges Bit: Stäbchen unten rechts
|
||||
* 8. höchstwertiges Bit: Stäbchen unten in der Mitte */
|
||||
pub const HEX: [u8; 16] = [
|
||||
0b1110111, // 0
|
||||
0b0010010, // 1
|
||||
@ -46,32 +65,57 @@ pub const HEX: [u8; 16] = [
|
||||
0b1101100, // F
|
||||
];
|
||||
|
||||
// findet die größte Hex-Zahl und gibt diese mit der Anzahl der Umlegungen zurück.
|
||||
pub fn largest_hex(filepath: &str) -> (Vec<DigitHistory>, u16) {
|
||||
let mut digits: Vec<DigitHistory>;
|
||||
let mut digits: Vec<DigitHistory>; // enthält die Ziffern und wie sie umgelegt wurden
|
||||
let max_swaps: u16;
|
||||
{
|
||||
let content = fs::read_to_string(filepath).unwrap();
|
||||
let content = fs::read_to_string(filepath).unwrap(); // liest die Eingabedatei in einen String
|
||||
let mut lines = content.lines();
|
||||
let line = lines.next().unwrap();
|
||||
digits = Vec::with_capacity(line.len());
|
||||
for character in line.chars() {
|
||||
let first_line = lines.next().unwrap();
|
||||
digits = Vec::with_capacity(first_line.len()); // die Länge der ersten Zeile ist die Anzahl der Ziffern
|
||||
for character in first_line.chars() { // für jedes Zeichen der ersten Zeile
|
||||
digits.push(DigitHistory {
|
||||
|
||||
// konvertiert die Ziffer in binäre Repräsentation
|
||||
original_digit: character.to_digit(16).unwrap() as u8,
|
||||
move_option: None,
|
||||
});
|
||||
}
|
||||
max_swaps = lines.next().unwrap().parse().unwrap();
|
||||
max_swaps = lines.next().unwrap().parse().unwrap(); // liest die Anzahl der maximalen Umlegungen aus
|
||||
}
|
||||
{
|
||||
// gibt an wie viele Stäbchen insgesamt hingelegt und weggenommenen wurden
|
||||
let mut total_swaps = PutTaken{ put: 0, taken: 0};
|
||||
for i in 0..digits.len() {
|
||||
for _ in 0..digits.len() { // wiederholt den Algorithmus
|
||||
|
||||
/* Wenn die maximalen Umlegungen schon ausgereizt sind, wurde die größte Hex-Zahl
|
||||
* gefunden. */
|
||||
if total_swaps.taken == total_swaps.put && total_swaps.put == max_swaps {break;}
|
||||
|
||||
/* beginnt von vorne an jede Ziffer durchzugehen und ändert jede auf die größtmögliche
|
||||
* Ziffer */
|
||||
total_swaps = change_digits(digits.iter_mut(), total_swaps, max_swaps, (), true, |_, _, _| {
|
||||
ChangeDigitsControl {change_digit: true, break_free: true}
|
||||
});
|
||||
|
||||
/* Wenn die Anzahl der hingelegten und weggenommenen Stäbchen ungleich ist, wird von
|
||||
* hinten an probiert dies auszugleichen. */
|
||||
if total_swaps.put != total_swaps.taken {
|
||||
total_swaps = change_digits(digits.iter_mut().rev(), total_swaps, max_swaps, i16::MAX, false, |new_total_swaps, total_swaps, best_diff| {
|
||||
|
||||
/* new_total_swaps ist die Anzahl der hingelegten und weggenommenen Stäbchen, wenn
|
||||
* man die Ziffer entsprechend ändert.
|
||||
* total_swaps ist die aktuelle Anzahl der hingelegten und weggenommenen Stäbchen.
|
||||
* best_diff ist der geringste Abstand zwischen der Anzahl der hingelegten und weggenommenen
|
||||
* Stäbchen. */
|
||||
total_swaps = change_digits(digits.iter_mut().rev(), total_swaps, max_swaps, i16::MAX, false,
|
||||
|new_total_swaps, total_swaps, best_diff| {
|
||||
|
||||
// Abstand, wenn man die Ziffer ändern würde
|
||||
let new_diff = (new_total_swaps.put as i16 - new_total_swaps.taken as i16).abs();
|
||||
|
||||
/* Wenn der neue Abstand kleiner ist als die alte, sollte man die Ziffer
|
||||
* ändern. */
|
||||
let change_digit = new_diff < (total_swaps.put as i16 - total_swaps.taken as i16).abs()
|
||||
&& new_diff < *best_diff;
|
||||
if change_digit {*best_diff = new_diff;}
|
||||
@ -79,11 +123,11 @@ pub fn largest_hex(filepath: &str) -> (Vec<DigitHistory>, u16) {
|
||||
});
|
||||
}
|
||||
}
|
||||
println!("taken {}, put {}", total_swaps.taken, total_swaps.put);
|
||||
(digits, max_swaps)
|
||||
}
|
||||
}
|
||||
|
||||
// konvertiert eine Slice von Ziffern zu einem hexadezimalen String
|
||||
pub fn to_hex_str(digits: &[DigitHistory]) -> String {
|
||||
let mut largest_hex_str = String::with_capacity(digits.len());
|
||||
for digit in digits {
|
||||
@ -92,19 +136,34 @@ pub fn to_hex_str(digits: &[DigitHistory]) -> String {
|
||||
largest_hex_str
|
||||
}
|
||||
|
||||
// generiert die konkreten Umlegungen
|
||||
pub fn gen_swaps(digits: &[DigitHistory], number_of_swaps: usize) -> (Vec<ChangedSticks>, Vec<ChangedSticks>) {
|
||||
|
||||
// Stäbchen, die hingelegt wurden
|
||||
let mut moves_put: Vec<ChangedSticks> = Vec::with_capacity(number_of_swaps);
|
||||
|
||||
// Stäbchen, die weggenommenen wurden
|
||||
let mut moves_taken: Vec<ChangedSticks> = Vec::with_capacity(number_of_swaps);
|
||||
|
||||
// für jede Ziffer
|
||||
for (j, DigitHistory {original_digit, move_option}) in digits.iter().enumerate() {
|
||||
if let Some(moved) = move_option {
|
||||
let from_hex = HEX[*original_digit as usize];
|
||||
if let Some(moved) = move_option { // Wenn die Ziffer geändert wurde
|
||||
let from_hex = HEX[*original_digit as usize]; // 7-Segmentrepräsentation der urspünglichen Ziffer
|
||||
|
||||
// 7-Segmentrepräsentation der Ziffer auf die sie geändert wurde
|
||||
let to_hex = HEX[moved.digit as usize];
|
||||
let changed = from_hex ^ to_hex;
|
||||
let put = changed & to_hex;
|
||||
let taken = changed & from_hex;
|
||||
let put = changed & to_hex; // Bitmap der hingelegten Stäbchen
|
||||
let taken = changed & from_hex; // Bitmap der weggenommenen Stäbchen
|
||||
|
||||
// für die hingelegten und weggenommenen Stäbchen
|
||||
for (put_or_taken, moves_put_or_taken) in [(put, &mut moves_put), (taken, &mut moves_taken)] {
|
||||
|
||||
// für jedes Stäbchen der Ziffer
|
||||
for i in 0..u8::BITS {
|
||||
let stick_map = put_or_taken & 1 << i;
|
||||
|
||||
// Wenn das Stäbchen geändert wurde, wird die Änderung in einem Vector gespeichert.
|
||||
if stick_map != 0 {
|
||||
moves_put_or_taken.push(ChangedSticks {
|
||||
digit_i: j,
|
||||
@ -118,31 +177,55 @@ pub fn gen_swaps(digits: &[DigitHistory], number_of_swaps: usize) -> (Vec<Change
|
||||
(moves_put, moves_taken)
|
||||
}
|
||||
|
||||
/* ändert die Ziffern mithilfe eines Iterators über diese. Eine Lambdafunktion evaluiert, ob die
|
||||
* Ziffer geändert werden soll.
|
||||
* init_t ist der Wert, auf den die Variable initialisiert wird, die der Lambdafunktion übergeben
|
||||
* wird.
|
||||
* Wenn higher_digit true ist, wird nur probiert, die Ziffer zu erhöhen. */
|
||||
fn change_digits<'a, I: Iterator<Item = &'a mut DigitHistory>, T: Copy,
|
||||
F: Fn(PutTaken, PutTaken, &mut T) -> ChangeDigitsControl>
|
||||
(iter: I, mut total_swaps: PutTaken, max_swaps: u16, init_t: T, higher_digit: bool, run: F) -> PutTaken {
|
||||
for DigitHistory {original_digit, move_option} in iter {
|
||||
|
||||
// bereits geänderte Ziffern werden nicht mehr erhöht
|
||||
if higher_digit && move_option.is_some() {continue;}
|
||||
|
||||
/* Anzahl der hingelegten und weggenommenen Stäbchen, wenn man die mögliche Änderung
|
||||
* der Ziffer rückgängig machen würde. */
|
||||
let mut swaps_with_undo = total_swaps;
|
||||
if let Some(moved) = move_option {
|
||||
swaps_with_undo -= moved.swaps;
|
||||
}
|
||||
let digit_hex = HEX[*original_digit as usize];
|
||||
let digit_hex = HEX[*original_digit as usize]; // 7-Segmentrepräsentation der urspünglichen Ziffer
|
||||
let mut best_move: Option<Move> = None;
|
||||
let mut t = init_t;
|
||||
|
||||
/* für jede Hex-Ziffer auf die man die Ziffer ändern könnte. Dabei wird bei der größten
|
||||
* Hex-Ziffer begonnen. */
|
||||
for (j, to_digit) in HEX.iter().enumerate().rev() {
|
||||
|
||||
/* Auf die gleich Ziffer zu ändern, ergibt keinen Sinn. Wenn nur auf höhere Ziffern
|
||||
* geändert werden soll, wird die Änderung abgebrochen, ansonsten mit der nächsten
|
||||
* Ziffer fortgefahren. */
|
||||
if j as u8 == *original_digit {
|
||||
if higher_digit {break;}
|
||||
else {continue;}
|
||||
}
|
||||
let changed_sticks = digit_hex ^ *to_digit;
|
||||
|
||||
// Anzahl der neu hingelegten und weggenommenen Stäbchen
|
||||
let swaps = PutTaken {
|
||||
put: (changed_sticks & *to_digit).count_ones() as u16,
|
||||
taken: (changed_sticks & digit_hex).count_ones() as u16,
|
||||
};
|
||||
// neue Gesamtzahl der hingelegten und weggenommenen Stäbchen
|
||||
let new_total_swaps = swaps_with_undo + swaps;
|
||||
|
||||
// Wenn die maximale Anzahl der Umlegungen nicht überschritten wurde
|
||||
if new_total_swaps.put <= max_swaps && new_total_swaps.taken <= max_swaps {
|
||||
let control = run(new_total_swaps, total_swaps, &mut t);
|
||||
|
||||
// Wenn die Ziffer geändert werden soll, wird dies als beste Änderung gespeichert.
|
||||
if control.change_digit {
|
||||
best_move = Some(Move {
|
||||
digit: j as u8,
|
||||
@ -152,6 +235,7 @@ fn change_digits<'a, I: Iterator<Item = &'a mut DigitHistory>, T: Copy,
|
||||
if control.break_free {break;}
|
||||
}
|
||||
}
|
||||
// führt die Änderung der Ziffer durch
|
||||
if let Some(moved) = best_move {
|
||||
*move_option = best_move;
|
||||
total_swaps = swaps_with_undo + moved.swaps;
|
||||
|
||||
@ -13,40 +13,71 @@ struct Args {
|
||||
/// Ausgabe der Anzahl der Umlegungen
|
||||
#[clap(short, long)]
|
||||
number_of_swaps_print: bool,
|
||||
|
||||
/// maximale Anzahl der Ziffern der 7-Segmentanzeige in einer Zeile
|
||||
#[clap(short, long, default_value_t = 20)]
|
||||
digits_per_line: u16,
|
||||
|
||||
/// gibt Latex-Code aus, um das Ergebnis des Programms einzubetten
|
||||
#[clap(short, long)]
|
||||
latex: bool,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
let args = Args::parse(); // liest die Argumente ein
|
||||
if args.latex {minted_begin();} // Anfang des einzubettenden Texts
|
||||
|
||||
// bildet die größte Hex-Zahl mithilfe der Eingabedatei
|
||||
let (digits, number_of_swaps) = hexmax::largest_hex(&args.filepath);
|
||||
println!("{}", hexmax::to_hex_str(&digits));
|
||||
println!("größte Hex-Zahl: {}", hexmax::to_hex_str(&digits)); // und gibt sie aus
|
||||
if args.number_of_swaps_print {println!("Anzahl der Umlegungen: {}", number_of_swaps);}
|
||||
if args.swap_print {
|
||||
|
||||
// generiert die Umlegungen
|
||||
let (moves_put, moves_taken) = hexmax::gen_swaps(&digits, number_of_swaps as usize);
|
||||
|
||||
// konvertiert die Ziffern zu der 7-Segmentrepräsentation
|
||||
let mut digits_hex: Vec<u8> = digits.iter()
|
||||
.map(|digit| hexmax::HEX[digit.original_digit as usize]).collect();
|
||||
|
||||
println!("Umlegungen:");
|
||||
swap_print(&digits_hex);
|
||||
println!();
|
||||
let mut not_first = false;
|
||||
for (move_put, move_taken) in moves_put.iter().zip(&moves_taken) {
|
||||
if not_first {println!();}
|
||||
swap_print(&digits_hex, args.digits_per_line); // gibt Startzahl als 7-Segmentrepräsentation aus
|
||||
for (i, (move_put, move_taken)) in moves_put.iter().zip(&moves_taken).enumerate() {
|
||||
println!();
|
||||
|
||||
// bettet jedes ASCII-Art extra ein, damit kein Seitenumbruch in einem ASCII-Art ist
|
||||
if args.latex {
|
||||
minted_end();
|
||||
minted_begin();
|
||||
}
|
||||
println!("Nach {}. Umlegung", i + 1);
|
||||
|
||||
// ändere die Zahl der Umlegung entsprechend
|
||||
for hexmax::ChangedSticks {digit_i, stick_map} in [move_put, move_taken] {
|
||||
digits_hex[*digit_i] ^= stick_map;
|
||||
if digits_hex[*digit_i] == 0 {
|
||||
panic!("Bei den generierten Umlegungen wurde eine Ziffer komplett geleert.");
|
||||
}
|
||||
}
|
||||
swap_print(&digits_hex);
|
||||
not_first = true;
|
||||
// gibt den neuen Zwischenstand aus
|
||||
swap_print(&digits_hex, args.digits_per_line);
|
||||
}
|
||||
}
|
||||
if args.latex {minted_end();} // Ende des einzubettenden Texts
|
||||
}
|
||||
|
||||
// gibt die 7-Segmentanzeige aus
|
||||
fn swap_print(digits_hex: &[u8], digits_per_line: u16) {
|
||||
for digits_line in digits_hex.chunks(digits_per_line as usize) {
|
||||
print_vertical(digits_line, 6);
|
||||
print_horizontal(digits_line, 5);
|
||||
print_vertical(digits_line, 3);
|
||||
print_horizontal(digits_line, 2);
|
||||
print_vertical(digits_line, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_print(digits_hex: &[u8]) {
|
||||
print_vertical(digits_hex, 6);
|
||||
print_horizontal(digits_hex, 5);
|
||||
print_vertical(digits_hex, 3);
|
||||
print_horizontal(digits_hex, 2);
|
||||
print_vertical(digits_hex, 0);
|
||||
}
|
||||
|
||||
// Hilfsfunktion für die Ausgabe der Zahlen. Sie ruft für jede Zahl die Lambdafunktion auf.
|
||||
fn print_helper<F: Fn(u8)>(digits_hex: &[u8], run: F) {
|
||||
let mut not_first = false;
|
||||
for digit in digits_hex {
|
||||
@ -57,16 +88,28 @@ fn print_helper<F: Fn(u8)>(digits_hex: &[u8], run: F) {
|
||||
println!();
|
||||
}
|
||||
|
||||
/* Wenn an der entsprechenden Position ein Stäbchen ist, wird das Zeichen, das das Stäbchen
|
||||
* repräsentiert zurückgegeben, ansonsten ein Leerzeichen. */
|
||||
fn print_stick(digit: u8, i: u8, stick_char: char) -> char {
|
||||
if digit >> i & 1 == 1 {stick_char} else {' '}
|
||||
}
|
||||
|
||||
// Funktion, um zwei horizontale, nebeneinander liegende Stäbchen auszugeben
|
||||
fn print_horizontal(digits_hex: &[u8], i: u8) {
|
||||
print_helper(digits_hex, |digit|
|
||||
print!("{} {}", print_stick(digit, i, '|'), print_stick(digit, i - 1, '|'))
|
||||
);
|
||||
}
|
||||
|
||||
// Funktion, die ein vertikal liegendes Stäbchen ausgibt
|
||||
fn print_vertical(digits_hex: &[u8], i: u8) {
|
||||
print_helper(digits_hex, |digit| print!(" {} ", print_stick(digit, i, '_')));
|
||||
}
|
||||
|
||||
fn minted_begin() {
|
||||
println!("{}", r"\begin{minted}[samepage]{text}");
|
||||
}
|
||||
|
||||
fn minted_end() {
|
||||
println!("{}", r"\end{minted}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user