basic a2
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,7 +1,7 @@
|
|||||||
# ---> Rust
|
# ---> Rust
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
/target/
|
target/
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
@ -11,3 +11,9 @@ Cargo.lock
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
beispieldaten/
|
beispieldaten/
|
||||||
|
|
||||||
|
# LaTex
|
||||||
|
*.aux
|
||||||
|
*.log
|
||||||
|
*.pdf
|
||||||
|
*.toc
|
||||||
|
|||||||
13
Aufgabe2-Rechenrätsel/Cargo.toml
Normal file
13
Aufgabe2-Rechenrätsel/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "rechenrätsel"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "3.0.10", features = ["derive"] }
|
||||||
|
num-derive = "0.3.3"
|
||||||
|
num-integer = "0.1.44"
|
||||||
|
num-traits = "0.2.14"
|
||||||
|
rand = "0.8.4"
|
||||||
119
Aufgabe2-Rechenrätsel/doc.tex
Normal file
119
Aufgabe2-Rechenrätsel/doc.tex
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
\documentclass[a4paper,10pt,ngerman]{scrartcl}
|
||||||
|
\usepackage{babel}
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage[utf8x]{inputenc}
|
||||||
|
\usepackage[a4paper,margin=2.5cm,footskip=0.5cm]{geometry}
|
||||||
|
|
||||||
|
% Die nächsten drei Felder bitte anpassen:
|
||||||
|
\newcommand{\Aufgabe}{Aufgabe 2: Rechenrätsel} % 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{listings}
|
||||||
|
\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}
|
||||||
|
\lstset{
|
||||||
|
keywordstyle=\color{blue},commentstyle=\color{mygreen},
|
||||||
|
stringstyle=\color{mymauve},rulecolor=\color{black},
|
||||||
|
basicstyle=\footnotesize\ttfamily,numberstyle=\tiny\color{mygray},
|
||||||
|
captionpos=b, % sets the caption-position to bottom
|
||||||
|
keepspaces=true, % keeps spaces in text
|
||||||
|
numbers=left, numbersep=5pt, showspaces=false,showstringspaces=true,
|
||||||
|
showtabs=false, stepnumber=2, tabsize=2, title=\lstname
|
||||||
|
}
|
||||||
|
\lstdefinelanguage{JavaScript}{ % JavaScript ist als einzige Sprache noch nicht vordefiniert
|
||||||
|
keywords={break, case, catch, continue, debugger, default, delete, do, else, finally, for, function, if, in, instanceof, new, return, switch, this, throw, try, typeof, var, void, while, with},
|
||||||
|
morecomment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]",
|
||||||
|
sensitive=true
|
||||||
|
}
|
||||||
|
|
||||||
|
% Anführungszeichen
|
||||||
|
\usepackage{csquotes}
|
||||||
|
|
||||||
|
% Diese beiden Pakete müssen zuletzt geladen werden
|
||||||
|
%\usepackage{hyperref} % Anklickbare Links im Dokument
|
||||||
|
\usepackage{cleveref}
|
||||||
|
|
||||||
|
% 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}
|
||||||
|
Die Aufgabenstellung gibt nicht vor wie ein Rätsel, das \enquote{interessant und
|
||||||
|
unterschiedlich} ist, zu sein hat. Damit das Rätsel interessant ist habe ich mir
|
||||||
|
folgende Regeln überlegt:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Bei $n$ Operatoren muss für $m_i$, die Anzahl, mit der jeder der
|
||||||
|
vier Operatoren vorkommt, gelten:
|
||||||
|
\begin{align}
|
||||||
|
\frac{n}{10} - 1 < m_i \leq \frac{n}{3} + 1
|
||||||
|
\end{align}
|
||||||
|
\item Bei $n$ Ziffern muss für $m_i$, die Anzahl, mit der jede der neun
|
||||||
|
Ziffern vorkommt, gelten:
|
||||||
|
\begin{align}
|
||||||
|
\frac{n}{16} - 1 < m_i \leq \frac{n}{4} + 1
|
||||||
|
\end{align}
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
Der Beweis, ob ein Rechenrätsel eindeutig lösbar ist, ist ein
|
||||||
|
Entscheidungsproblem mit NP-Schwere. Eine Lösung ist die Ziffern zufällig zu
|
||||||
|
wählen und alle möglichen Kombinationen von Operatoren auszuprobieren. Kommt ein
|
||||||
|
Ergebnis nur einmal vor, wurde ein eindeutiges Rätsel gefunden, was in der Regel
|
||||||
|
der Fall sein sollte. Solch eine Brute-Force Operation dauert allerdings bei
|
||||||
|
viele Operatoren exponentiell länger. Die Anzahl der Möglichkeiten $|\Omega|$
|
||||||
|
kann in Abhängigkeit von der Operatorenanzahl $n$ berechnet werden:
|
||||||
|
$|\Omega|=4^{n}$. 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.
|
||||||
|
|
||||||
|
\section{Umsetzung}
|
||||||
|
Hier wird kurz erläutert, wie die Lösungsidee im Programm tatsächlich umgesetzt wurde. Hier können auch Implementierungsdetails erwähnt werden.
|
||||||
|
|
||||||
|
\section{Beispiele}
|
||||||
|
Genügend Beispiele einbinden! Die Beispiele von der BwInf-Webseite sollten hier diskutiert werden, aber auch eigene Beispiele sind sehr gut – besonders wenn sie Spezialfälle abdecken. Aber bitte nicht 30 Seiten Programmausgabe hier einfügen!
|
||||||
|
|
||||||
|
\section{Quellcode}
|
||||||
|
Unwichtige Teile des Programms sollen hier nicht abgedruckt werden. Dieser Teil sollte nicht mehr als 2–3 Seiten umfassen, maximal 10.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\end{document}
|
||||||
222
Aufgabe2-Rechenrätsel/src/main.rs
Normal file
222
Aufgabe2-Rechenrätsel/src/main.rs
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use rand::distributions::{Distribution, Uniform};
|
||||||
|
use num_derive::FromPrimitive;
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
#[derive(FromPrimitive)]
|
||||||
|
enum Operator {
|
||||||
|
Add,
|
||||||
|
Subtract,
|
||||||
|
Multiplicate,
|
||||||
|
Divide,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromPrimitive, PartialEq, Clone, Copy)]
|
||||||
|
enum AddSubOrMultiplicateDivide {
|
||||||
|
AddSub,
|
||||||
|
MultiplicateDivide,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[clap(author, version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// maximale Anzahl der auszugebenden Lösungen
|
||||||
|
#[clap(short, long, default_value_t = 1)]
|
||||||
|
maximum: usize,
|
||||||
|
|
||||||
|
/// Anzahl der Operatoren
|
||||||
|
#[clap(short, long, default_value_t = 5)]
|
||||||
|
count: u8,
|
||||||
|
|
||||||
|
/// zeigt die Lösung an
|
||||||
|
#[clap(short, long, takes_value = false)]
|
||||||
|
solution_print: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
type RiddleMap = HashMap<i64, u64>;
|
||||||
|
|
||||||
|
struct PartOperation {
|
||||||
|
results: RiddleMap,
|
||||||
|
last_operator: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ResultStore {
|
||||||
|
riddles: RiddleMap,
|
||||||
|
results_already_taken: HashSet<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResultStore {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self{
|
||||||
|
riddles: HashMap::new(),
|
||||||
|
results_already_taken: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn store(&mut self, result: i64, operators: u64) {
|
||||||
|
if self.results_already_taken.contains(&result) {return;}
|
||||||
|
match self.riddles.remove(&result) {
|
||||||
|
None => {self.riddles.insert(result, operators);}
|
||||||
|
Some(_x) => {self.results_already_taken.insert(result);}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
let mut digits: Vec<u8> = vec!(0; (args.count + 1) as usize);
|
||||||
|
{
|
||||||
|
let mut selectable_digits: Vec<u8> = Vec::from_iter(0..9);
|
||||||
|
let mut digits_count: [u8; 9] = [0; 9];
|
||||||
|
let min: u8 = digits.len() as u8 / 16;
|
||||||
|
let max: u8 = digits.len() as u8 / 4 + 1;
|
||||||
|
let mut left_for_non_min: u8 = digits.len() as u8 - 9 * min;
|
||||||
|
let mut digits_min_statisfied: HashSet<u8> = HashSet::with_capacity(9);
|
||||||
|
for digit in digits.iter_mut() {
|
||||||
|
if left_for_non_min == 0 {
|
||||||
|
selectable_digits.retain(|x| !digits_min_statisfied.contains(x));
|
||||||
|
}
|
||||||
|
let rand_i = Uniform::new(0, selectable_digits.len()).sample(&mut rand::thread_rng());
|
||||||
|
let rand_number = selectable_digits[rand_i];
|
||||||
|
digits_count[rand_number as usize] += 1;
|
||||||
|
let digit_used = digits_count[rand_number as usize];
|
||||||
|
if digit_used == min {
|
||||||
|
if left_for_non_min == 0 {
|
||||||
|
selectable_digits.remove(rand_i);
|
||||||
|
} else {digits_min_statisfied.insert(rand_number);}
|
||||||
|
}
|
||||||
|
if digit_used > min {left_for_non_min -= 1;}
|
||||||
|
if digit_used == max {selectable_digits.remove(rand_i);}
|
||||||
|
*digit = rand_number + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut results = ResultStore::new();
|
||||||
|
for dm_as_map in 0..2u64.pow(args.count as u32) {
|
||||||
|
let mut results_multiplicate: Vec<PartOperation> = Vec::new();
|
||||||
|
{
|
||||||
|
let mut last_i: usize = 0;
|
||||||
|
let mut i: usize;
|
||||||
|
let mut state: AddSubOrMultiplicateDivide = FromPrimitive::from_u64(dm_as_map & 1).unwrap();
|
||||||
|
while last_i < args.count as usize {
|
||||||
|
i = last_i;
|
||||||
|
while i < args.count as usize && dm_as_map >> i & 1 == state as u64 {
|
||||||
|
if state == AddSubOrMultiplicateDivide::AddSub {
|
||||||
|
results_multiplicate.push(insert_digit(&digits, i));
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
if state == AddSubOrMultiplicateDivide::MultiplicateDivide {
|
||||||
|
let mut part_results = ResultStore::new();
|
||||||
|
i += 1;
|
||||||
|
let mut digits_calc = digits[last_i .. i].iter();
|
||||||
|
let first_digit = *digits_calc.next().unwrap() as i64;
|
||||||
|
calc_part(digits_calc, &mut part_results, last_i as u8 * 2, first_digit, 0);
|
||||||
|
results_multiplicate.push(PartOperation{
|
||||||
|
results: part_results.riddles,
|
||||||
|
last_operator: last_operator_helper(last_i),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
state = match state {
|
||||||
|
AddSubOrMultiplicateDivide::MultiplicateDivide => AddSubOrMultiplicateDivide::AddSub,
|
||||||
|
AddSubOrMultiplicateDivide::AddSub => AddSubOrMultiplicateDivide::MultiplicateDivide,
|
||||||
|
};
|
||||||
|
last_i = i;
|
||||||
|
}
|
||||||
|
if dm_as_map >> args.count - 1 & 1 == AddSubOrMultiplicateDivide::AddSub as u64 {
|
||||||
|
results_multiplicate.push(insert_digit(&digits, digits.len() - 1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut iter = results_multiplicate.iter();
|
||||||
|
for (part_result, operators) in &iter.next().unwrap().results {
|
||||||
|
add_sub(iter.clone(), &mut results, *part_result, *operators);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut not_first = false;
|
||||||
|
for (result, operators) in results.riddles {
|
||||||
|
if not_first {println!();}
|
||||||
|
not_first = true;
|
||||||
|
print_riddle(&digits, false, result, operators);
|
||||||
|
if args.solution_print {
|
||||||
|
print_riddle(&digits, true, result, operators);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_part<'a>(mut iter: impl Clone + Iterator<Item = &'a u8>, results: &mut ResultStore, operator_index: u8, part_result: i64, operators: u64) {
|
||||||
|
match iter.next() {
|
||||||
|
None => results.store(part_result, operators),
|
||||||
|
Some(next) => {
|
||||||
|
let next_digit = *next as i64;
|
||||||
|
for operator in [Operator::Multiplicate, Operator::Divide] {
|
||||||
|
calc_part(iter.clone(), results, operator_index + 2,
|
||||||
|
match operator {
|
||||||
|
Operator::Multiplicate => part_result * next_digit,
|
||||||
|
Operator::Divide => {
|
||||||
|
if part_result % next_digit != 0 {continue;}
|
||||||
|
part_result / next_digit
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
},
|
||||||
|
operators | (operator as u64) << operator_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_sub<'a>(mut iter: impl Clone + Iterator<Item = &'a PartOperation>, results: &mut ResultStore, part_result: i64, operators: u64) {
|
||||||
|
match iter.next() {
|
||||||
|
None => {
|
||||||
|
if part_result > 0 {results.store(part_result, operators);}
|
||||||
|
}
|
||||||
|
Some(next) => {
|
||||||
|
for (next_result, part_operators) in &next.results {
|
||||||
|
for operator in [Operator::Add, Operator::Subtract] {
|
||||||
|
add_sub(iter.clone(), results,
|
||||||
|
match operator {
|
||||||
|
Operator::Add => part_result + next_result,
|
||||||
|
Operator::Subtract => part_result - next_result,
|
||||||
|
_ => 0,
|
||||||
|
},
|
||||||
|
operators | part_operators | (operator as u64) << 2 * next.last_operator.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_operator_helper(index: usize) -> Option<usize> {
|
||||||
|
if index == 0 {None} else {Some(index as usize - 1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_digit(digits: &Vec<u8>, i: usize) -> PartOperation {
|
||||||
|
let mut result_map = RiddleMap::new();
|
||||||
|
result_map.insert(digits[i] as i64, 0);
|
||||||
|
return PartOperation{
|
||||||
|
results: result_map,
|
||||||
|
last_operator: last_operator_helper(i),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_riddle(digits: &Vec<u8>, show_solution: bool, result: i64, operators: u64) {
|
||||||
|
print!("{}: ", if show_solution {"Lösung"} else {"Rätsel"});
|
||||||
|
let mut i = 0;
|
||||||
|
for digit in digits[.. digits.len() - 1].iter() {
|
||||||
|
print!("{} {} ", digit, if show_solution {
|
||||||
|
match FromPrimitive::from_u64(operators >> i & 3).unwrap() {
|
||||||
|
Operator::Multiplicate => '*',
|
||||||
|
Operator::Divide => ':',
|
||||||
|
Operator::Add => '+',
|
||||||
|
Operator::Subtract => '-',
|
||||||
|
}
|
||||||
|
} else {'○'});
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
println!("{} = {}", digits.last().unwrap(), result);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user