summary refs log tree commit diff
path: root/bonusaufgabe/src
diff options
context:
space:
mode:
authorMalte Voos <git@mal.tc>2022-08-20 14:12:11 +0200
committerMalte Voos <git@mal.tc>2022-08-20 14:12:11 +0200
commitd0c70ad88b11f412c9d8c6735b74cce1b1ff015b (patch)
tree2f0e9821a785e083abfe851ce919cadbefe001af /bonusaufgabe/src
parent5f22745507a343163521fbe85cdc72ac144c319f (diff)
downloadbwinf402-main.tar.gz
bwinf402-main.zip
cleanup main
Diffstat (limited to 'bonusaufgabe/src')
-rw-r--r--bonusaufgabe/src/benchmark.rs24
-rw-r--r--bonusaufgabe/src/main.rs103
2 files changed, 72 insertions, 55 deletions
diff --git a/bonusaufgabe/src/benchmark.rs b/bonusaufgabe/src/benchmark.rs
new file mode 100644
index 0000000..9063977
--- /dev/null
+++ b/bonusaufgabe/src/benchmark.rs
@@ -0,0 +1,24 @@
+use super::*;
+use test::Bencher;
+
+fn benchmark(task_file_name: &str, bencher: &mut Bencher) {
+    let task_str = fs::read_to_string(task_file_name).expect("Datei kann nicht gelesen werden");
+    let task = Task::try_from(task_str.as_str()).expect("Datei enthält keine gültige Aufgabe");
+
+    bencher.iter(|| solve_task(&task));
+}
+
+#[bench]
+fn benchmark3(bencher: &mut Bencher) {
+    benchmark("input/stapel3.txt", bencher);
+}
+
+#[bench]
+fn benchmark4(bencher: &mut Bencher) {
+    benchmark("input/stapel4.txt", bencher);
+}
+
+#[bench]
+fn benchmark5(bencher: &mut Bencher) {
+    benchmark("input/stapel5.txt", bencher);
+}
diff --git a/bonusaufgabe/src/main.rs b/bonusaufgabe/src/main.rs
index f5983bc..f3245d2 100644
--- a/bonusaufgabe/src/main.rs
+++ b/bonusaufgabe/src/main.rs
@@ -1,14 +1,21 @@
+#![feature(test)]
+
+extern crate test;
+
+#[cfg(test)]
+mod benchmark;
+
 use bitvec::prelude::*;
 use rand::seq::SliceRandom;
 use std::env;
 use std::fmt;
 use std::fs;
 use std::process;
+use std::time;
 
 type Card = BitVec;
 
 // Typ, der eine zu lösende Aufgabe beschreibt.
-#[derive(Clone)] // TODO remove
 struct Task {
     cards: Vec<Card>,
     num_pass_cards: usize,
@@ -17,11 +24,18 @@ struct Task {
 // Die Lösung enthält die von Zara stammenden echten Karten.
 struct Solution {
     real_cards: Vec<Card>,
+    // Der Anschaulichkeit halber werden ein paar weitere Randinformationen
+    // mit ausgegeben.
+    num_cards: usize,
+    num_real_cards: usize,
+    bits_per_card: usize,
+    num_free_vars: usize,
+    p: usize,
 }
 
 // `solve_task` löst eine gegebene Aufgabe und beinhaltet den
 // eigentlichen Algorithmus.
-fn solve_task(task: Task) -> Solution {
+fn solve_task(task: &Task) -> Solution {
     // `num_real_cards` Karten im Stapel stammen von Zara Zackig, nämlich
     // die `num_pass_cards` Öffnungskarten plus eine Sicherungkarte.
     let num_real_cards = task.num_pass_cards + 1;
@@ -76,28 +90,20 @@ fn lee_brickell_iteration(cards: &[Card], num_real_cards: usize) -> Option<Solut
     // `ppcm` ist eine solche Kontrollmatrix (repräsentiert als Iliffe-Vektor),
     // auf dessen Spalten zusätzlich noch die Permutation `permutation`
     // angewandt wurde.
-    //
-    // O(bits_per_card num_cards)
     let mut ppcm = transpose_and_optionally_permute_columns(cards, Some(&permutation));
 
     // `basic_vars` und `free_vars` enthalten jeweils die Spaltenindizes der
-    // Basisvariablen bzw. freien Variablen von `ppcm`. Die Indizes der
-    // Basisvariablen bilden das "information set" (siehe Dokumentation)
-    // dieser Iteration.
+    // Basisvariablen bzw. freien Variablen von `ppcm`.
     let mut basic_vars = Vec::new();
     let mut free_vars = Vec::new();
 
     // Zunächst wird `ppcm` mittels des Gaußschen Eliminierungsverfahrens
     // in die reduzierte Stufenform gebracht.
-    //
-    // O(bits_per_card^2 num_cards)
     let mut current_row = 0;
     let mut current_col = 0;
     while current_row < bits_per_card && current_col < num_cards {
-        // O(bits_per_card)
         // Wir suchen in der derzeitigen Spalte ein Pivotelement (eine 1).
         let pivot_row = match (current_row..bits_per_card).find(|row| ppcm[*row][current_col]) {
-            // O(bits_per_card) worst-case
             Some(row) => row,
             None => {
                 // Wurde kein Pivotelement gefunden, gehört diese Spalte zu
@@ -118,10 +124,9 @@ fn lee_brickell_iteration(cards: &[Card], num_real_cards: usize) -> Option<Solut
         // indem die entsprechenden Zeilen mit der Zeile des Pivotelements XOR
         // gerechnet (addiert) werden.
         for lower_row in (current_row + 1)..bits_per_card {
-            // O(bits_per_card)
             if ppcm[lower_row][current_col] {
                 let current_row_cloned = ppcm[current_row].clone();
-                ppcm[lower_row] ^= current_row_cloned; // O(num_cards)
+                ppcm[lower_row] ^= current_row_cloned;
             }
         }
         // Es kann zur nächsten Zeile und Spalte übergegangen werden.
@@ -145,15 +150,11 @@ fn lee_brickell_iteration(cards: &[Card], num_real_cards: usize) -> Option<Solut
     // Einsen werden eliminiert, indem die entsprechenden Zeilen mit der Zeile
     // des Pivotelements XOR gerechnet (addiert) werden. Diese Erweiterung
     // wird auch als Gauß-Jordan-Algorithmus bezeichnet.
-    //
-    // O(bits_per_card^2 num_cards)
     for (pivot_row, pivot_col) in basic_vars.iter().enumerate().rev() {
-        // O(bits_per_card)
         for upper_row in 0..pivot_row {
-            // O(bits_per_card)
             if ppcm[upper_row][*pivot_col] {
                 let pivot_row_cloned = ppcm[pivot_row].clone();
-                ppcm[upper_row] ^= pivot_row_cloned; // O(num_cards)
+                ppcm[upper_row] ^= pivot_row_cloned;
             }
         }
     }
@@ -164,25 +165,24 @@ fn lee_brickell_iteration(cards: &[Card], num_real_cards: usize) -> Option<Solut
     // den Wert 1 haben. Ob diese Annahme stimmt, hängt davon ab, ob im ersten
     // Schritt eine glückliche Permutation gewählt wurde.
 
-    // Bei sehr einfachen Aufgabenstellungen kann es vorkommen, das die Anzahl
-    // der freien Variablen noch kleiner als Parameter `P` des Lee-Brickell-
-    // Algorithmus ist (z.B. bei nur einer freien Variable). In diesem Fall
-    // muss der Parameter natürlich entsprechend nach unten korrigiert werden.
-    let p = P.min(num_free_vars);
+    // In einigen Fällen muss der Parameter `P` des Lee-Brickell-Algorithmus
+    // an die Eingabe angepasst werden, z.B. bei sehr leicht lösbaren Aufgaben
+    // mit nur einer freien Variable. Details sind in der Dokumentation,
+    // Abschnitt 1.2, Schritt 4 zu finden.
+    let p = ((P as isize).clamp(
+        -(num_cards as isize) + num_free_vars as isize + num_real_cards as isize,
+        num_real_cards.min(num_free_vars) as isize,
+    )) as usize;
 
     // Zunächst wird die Matrix transponiert, damit die Bits der Spalten
     // jeweils zusammenhängend im Speicher liegen und somit die Spalten
     // schneller miteinander XOR gerechnet werden können.
-    //
-    // O(bits_per_card num_cards)
     let transposed_ppcm = transpose_and_optionally_permute_columns(&ppcm, None);
 
     // Um die `p` freien Variablen mit dem Wert 1 zu finden, wird über alle
     // möglichen `p`-Untermengen der freien Variablen iteriert und jeweils
     // angenommen, dass von den freien Variablen nur die in der Untermenge den
     // Wert 1 haben.
-    //
-    // O(k choose p)
     let mut subset_iter = SubsetIterator::new(num_free_vars, p);
     while let Some(subset) = subset_iter.next() {
         // Zunächst wird das exklusive Oder der `p` Spalten in der Untermenge
@@ -216,7 +216,14 @@ fn lee_brickell_iteration(cards: &[Card], num_real_cards: usize) -> Option<Solut
             // echten Karten vor der Ausgabe aufsteigend sortiert.
             real_cards.sort();
 
-            return Some(Solution { real_cards });
+            return Some(Solution {
+                real_cards,
+                num_cards,
+                num_real_cards,
+                bits_per_card,
+                num_free_vars,
+                p,
+            });
         }
     }
 
@@ -334,33 +341,12 @@ fn main() {
     let task_str = fs::read_to_string(task_file_name).expect("Datei kann nicht gelesen werden");
     let task = Task::try_from(task_str.as_str()).expect("Datei enthält keine gültige Aufgabe");
 
-    // let solution = solve_task(task);
-    // print!("{}", solution);
-
-    let mut task = task;
+    let start = time::Instant::now();
+    let solution = solve_task(&task);
+    let elapsed = start.elapsed();
 
-    loop {
-        use rand::prelude::*;
-        let solution = solve_task(task.clone());
-        print!("{}", solution);
-
-        let num_rand_cards = task.cards.len() - task.num_pass_cards - 1;
-        let bits_per_card = task.cards[0].len();
-        let mut new_rand_cards = (0..num_rand_cards)
-            .map(|_| {
-                rand::thread_rng()
-                    .sample_iter::<bool, rand::distributions::Standard>(
-                        rand::distributions::Standard,
-                    )
-                    .take(bits_per_card)
-                    .collect::<BitVec>()
-            })
-            .collect::<Vec<BitVec>>();
-        let mut new_cards = solution.real_cards;
-        new_cards.append(&mut new_rand_cards);
-        new_cards.shuffle(&mut rand::thread_rng());
-        task.cards = new_cards
-    }
+    println!("{}", solution);
+    println!("Laufzeit ohne I/O: {:?}", elapsed);
 }
 
 // Liest eine Aufgabe im Format der Beispielaufgaben ein.
@@ -386,7 +372,7 @@ impl TryFrom<&str> for Task {
                         '1' => Some(true),
                         _ => None,
                     })
-                    .collect::<BitVec>()
+                    .collect::<Card>()
             })
             .collect::<Vec<Card>>();
 
@@ -404,6 +390,13 @@ impl TryFrom<&str> for Task {
 // Formatiert die Lösung zur Ausgabe.
 impl fmt::Display for Solution {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        writeln!(
+            f,
+            "Randinformationen (siehe Dokumentation):\nn = {}; w = {}; b = {}; k = {}; p = {}",
+            self.num_cards, self.num_real_cards, self.bits_per_card, self.num_free_vars, self.p
+        )?;
+        writeln!(f)?;
+        writeln!(f, "Echte Karten:")?;
         for card in self.real_cards.iter() {
             for bit in card.iter() {
                 match *bit {