final commit

This commit is contained in:
2022-04-25 13:08:42 +02:00
parent d98e4dbd31
commit a9eda356bf
17 changed files with 813 additions and 128 deletions

View File

@ -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 23 Seiten umfassen, maximal 10.
src/lib.rs:
\inputminted[breaklines,fontsize=\footnotesize]{rs}{src/lib.rs}
\end{document}

View File

@ -0,0 +1 @@
größte Hex-Zahl: EE4

View File

@ -0,0 +1 @@
größte Hex-Zahl: FFFEA97B55

View File

@ -0,0 +1 @@
größte Hex-Zahl: FFFFFFFFFFFFFFFFD9A9BEAEE8EDA8BDA989D9F8

View File

@ -0,0 +1 @@
größte Hex-Zahl: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA98BB8B9DFAFEAE888DD888AD8BA8EA8888

View File

@ -0,0 +1 @@
größte Hex-Zahl: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB8DE88BAA8ADD888898E9BA88AD98988F898AB7AF7BDA8A61BA7D4AD8F888

View File

@ -0,0 +1 @@
größte Hex-Zahl: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88EFA9EBE89EFA99FBDAA8E8EAD88AB899F8E8F9AA9E9AD88988EDA9A99888EDAD989A8BAFD8A888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

View File

@ -0,0 +1 @@
größte Hex-Zahl: ED8

View File

@ -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;

View File

@ -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}");
}