diff --git a/Uebung-02/dice.py b/Uebung-02/dice.py index 4db4e73..52ac636 100644 --- a/Uebung-02/dice.py +++ b/Uebung-02/dice.py @@ -17,7 +17,7 @@ dice_props = [0.1, 0.1, 0.1, 0.1, 0.1, 0.5] coin_sides = ['Head', 'Number'] coin_head = [0.1, 0.5] -rep = 100000 +rep = 100 # --- Dice test --- res_cpp, acc_dice, rej_dice = throw_dice_cpp(rep, dice_sides, dice_props) diff --git a/Uebung-03/find.py b/Uebung-03/find.py new file mode 100644 index 0000000..675b0e5 --- /dev/null +++ b/Uebung-03/find.py @@ -0,0 +1,59 @@ +import math +import random +from multiprocessing import Pool +from typing import Optional, Iterable, Tuple + +def gen_nums(n: int) -> list[int]: + assert n > 0 + nums = list(range(2 ** n)) + random.shuffle(nums) + return nums + +def chunks(n: int, p: int) -> Iterable[Tuple[int, int]]: + base, rem = divmod(n, p) + start = 0 + for i in range(p): + size = base + (1 if i < rem else 0) + end = start + size + yield start, end + start = end + +def t_find(args: Tuple[int, int, list[int], int]) -> Optional[int]: + s, e, haystack, needle = args + for i in range(s, e): + if haystack[i] == needle: + return i + return None + +def find(needle: int, haystack: list[int]) -> Optional[int]: + size = len(haystack) + assert size >= 1 + + procs = int(math.log2(size)) + ranges = list(chunks(size, procs)) + with Pool(processes=procs) as pool: + res = pool.map(t_find, [(s, e, haystack, needle) for (s, e) in ranges]) + + for idx in res: + if idx is not None: + return idx + return None + +def fuzz_test(trials: int = 200, max_n: int = 16, seed: Optional[int] = None) -> None: + if seed is not None: + random.seed(seed) + + for t in range(1, trials + 1): + n = random.randint(1, max_n) + arr = gen_nums(n) + + idx_true = random.randrange(len(arr)) + needle = arr[idx_true] + expected = idx_true + + got = find(needle, arr) + assert got == expected, f"[t={t}] Expected {expected}, got {got}; n={n}" + + print(f"Fuzz OK: {trials} trials, max_n={max_n}") + +fuzz_test(trials=1000, max_n=16) \ No newline at end of file diff --git a/Uebung-10/BitVector.hpp b/Uebung-10/BitVector.hpp new file mode 100644 index 0000000..36ba40d --- /dev/null +++ b/Uebung-10/BitVector.hpp @@ -0,0 +1,126 @@ +#pragma once +#include +#include +#include +#include + +class BitVector { + std::vector data; + std::vector rankIndex; + uint64_t numBits; + bool isBuilt = false; + static constexpr uint64_t BLOCK_SIZE_BITS = 512; + static constexpr uint64_t WORDS_PER_BLOCK = BLOCK_SIZE_BITS / 64; + + static uint64_t countSetBits(uint64_t n) { + return std::popcount(n); + } + +public: + explicit BitVector(uint64_t n) : numBits(n) { + data.resize((n + 63) / 64, 0); + } + + // Bit setzen, nur während der Initialisierung erlaubt + void setBit(uint64_t index) { + if (isBuilt) throw std::runtime_error("BitVector is already built (read-only)."); + if (index >= numBits) throw std::out_of_range("Bit index out of range."); + data[index / 64] |= 1ULL << (index % 64); + } + + // Index aufbauen, muss nach dem Setzen aller Bits und vor der ersten Abfrage gerufen werden + void buildIndex() { + if (isBuilt) return; + rankIndex.clear(); + rankIndex.reserve(data.size() / WORDS_PER_BLOCK + 2); + + uint32_t currentRank = 0; + for (size_t i = 0; i < data.size(); ++i) { + if (i % WORDS_PER_BLOCK == 0) { + rankIndex.push_back(currentRank); + } + currentRank += countSetBits(data[i]); + } + rankIndex.push_back(currentRank); + isBuilt = true; + } + + // Anzahl der Einsen im Bereich [0, index) + uint64_t rank1(uint64_t index) const { + if (!isBuilt) throw std::runtime_error("Index not built."); + if (index > numBits) index = numBits; + + uint64_t blockIdx = index / BLOCK_SIZE_BITS; + uint64_t result = rankIndex[blockIdx]; + uint64_t startWord = blockIdx * WORDS_PER_BLOCK; + uint64_t endWord = index / 64; + + for (uint64_t i = startWord; i < endWord; ++i) { + result += countSetBits(data[i]); + } + + uint64_t bitsInLastWord = index % 64; + if (bitsInLastWord > 0) { + result += countSetBits(data[endWord] & ((1ULL << bitsInLastWord) - 1)); + } + return result; + } + + uint64_t rank0(uint64_t index) const { + return index - rank1(index); + } + + // Gibt den Index der k-ten Eins zurück (k ist 1-basiert) + uint64_t select1(uint64_t k) const { + if (!isBuilt) throw std::runtime_error("Index not built."); + if (k == 0) return -1; + + auto it = std::upper_bound(rankIndex.begin(), rankIndex.end(), k); + uint64_t blockIdx = std::distance(rankIndex.begin(), it) - 1; + uint64_t currentRank = rankIndex[blockIdx]; + uint64_t wordIdx = blockIdx * WORDS_PER_BLOCK; + + while (wordIdx < data.size()) { + uint64_t count = countSetBits(data[wordIdx]); + if (currentRank + count >= k) { + uint64_t temp = data[wordIdx]; + for (int bit = 0; bit < 64; ++bit) { + if ((temp >> bit) & 1) { + currentRank++; + if (currentRank == k) return wordIdx * 64 + bit; + } + } + } + currentRank += count; + wordIdx++; + } + return -1; // Nicht gefunden + } + + // Gibt den Index der k-ten Null zurück (k ist 1-basiert) + uint64_t select0(uint64_t k) const { + if (!isBuilt) throw std::runtime_error("Index not built."); + // Binäre Suche über rank0 + uint64_t low = 0, high = numBits; + uint64_t ans = -1; + while (low <= high) { + uint64_t mid = low + (high - low) / 2; + if (rank0(mid + 1) >= k) { + ans = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return ans; + } + + // Speicherverbrauch in Bytes (inkl. Overhead) + uint64_t sizeInBytes() const { + return (data.capacity() * sizeof(uint64_t)) + + (rankIndex.capacity() * sizeof(uint32_t)) + + sizeof(*this); + } + + uint64_t length() const { return numBits; } +}; diff --git a/Uebung-10/main b/Uebung-10/main new file mode 100755 index 0000000..453fada Binary files /dev/null and b/Uebung-10/main differ diff --git a/Uebung-10/main.cpp b/Uebung-10/main.cpp new file mode 100644 index 0000000..e81e7a2 --- /dev/null +++ b/Uebung-10/main.cpp @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include + +#include "BitVector.hpp" + +// ========================================== +// Aufgabenteil a) Baseline-Implementierung +// ========================================== + +class BaselineTree { +public: + struct Node { + uint32_t parent = 0; // 0 = Root + std::vector children; + }; + +private: + std::vector nodes; + +public: + BaselineTree() { + nodes.emplace_back(); + nodes.emplace_back(); + } + + static int32_t root() { + return 1; + } + + [[nodiscard]] uint64_t nodeCount() const { + return nodes.size() - 1; + } + + [[nodiscard]] const std::vector& children(const uint32_t id) const { + return nodes[id].children; + } + + [[nodiscard]] uint32_t parent(const uint32_t id) const { + return nodes[id].parent; + } + + [[nodiscard]] bool exists(const uint32_t id) const { + return id > 0 && id < nodes.size(); + } + + uint32_t add(const uint32_t parentId) { + if (!exists(parentId)) { + throw std::invalid_argument("parentId does not exist"); + } + + nodes.emplace_back(); + const uint32_t id = nodes.size() - 1; + + nodes[id].parent = parentId; + nodes[parentId].children.push_back(id); + + return id; + } + + // BFS traversing with lambda for increased reusability + template + void bfs(Fn&& fn) const { + std::queue q; + q.push(root()); + + while (!q.empty()) { + uint32_t u = q.front(); + q.pop(); + + fn(u, nodes[u]); + + for (uint32_t v : nodes[u].children) { + q.push(v); + } + } + } + + [[nodiscard]] uint64_t sizeInBytes() const { + uint64_t total = 0; + + // Size of data in the tree instance + total += sizeof(BaselineTree); + total += nodes.capacity() * sizeof(Node); + + // Size of data in node instances + bfs([&](uint32_t, const Node& n) { + total += n.children.capacity() * sizeof(uint32_t); + }); + + return total; + } + + static BaselineTree randomTree(const uint64_t N, const uint32_t seed = 03062001) { + BaselineTree t; + t.nodes.reserve(N + 1); + + std::mt19937 gen(seed); + for (uint32_t i = 2; i <= N; ++i) { + std::uniform_int_distribution dist(1, i - 1); + t.add(dist(gen)); + } + + return t; + } +}; + + + +// ========================================== +// Aufgabenteil b) LOUDS-Implementierung +// ========================================== + +class LOUDSTree { + // Der BitVector ist der einzige Datenspeicher! Keine Knoten-Objekte. + BitVector bv; + +public: + // TODO: Passen Sie den Konstruktor an Ihre BaselineTree-Klasse an. + // Der Konstruktor soll den übergebenen Baum in Level-Order traversieren + // und die LOUDS-Bits in 'bv' setzen. + explicit LOUDSTree(const BaselineTree &tree) : bv(1 /* TODO: Richtige Größe berechnen! */) { + // TODO: Implementierung der LOUDS-Erstellung (BFS Traversierung) + // TODO: bfs can be used here! Comment by JNLOOS + + // WICHTIG: Am Ende muss der Index gebaut werden: + bv.buildIndex(); + } + + uint64_t sizeInBytes() const { + return bv.sizeInBytes(); + } + + // --- LOUDS-Operationen (Ausschließlich via rank/select implementieren!) --- + + uint64_t parent(uint64_t i) { + // TODO + return 0; + } + + bool isRoot(uint64_t i) { + // TODO + return false; + } + + bool isLeaf(uint64_t i) { + // TODO + return false; + } + + uint64_t outDegree(uint64_t i) { + // TODO + return 0; + } + + uint64_t childNum(uint64_t i, uint64_t j) { + // TODO: Geben Sie das j-te Kind von nodeId zurück (j ist 1-basiert) + return 0; + } +}; + + +int main() { + // Parameter für den Benchmark, 1 Million Knoten + const uint64_t N = 1000000; + + std::cout << "--- Start der Uebung: LOUDS (N=" << N << ") ---" << std::endl; + + // 1. Baseline Tree erstellen + std::cout << "[Init] Erstelle Baseline Tree..." << std::endl; + // Nutzen Sie std::mt19937 für reproduzierbare Zufallszahlen. + const BaselineTree baseline = BaselineTree::randomTree(N); + + // 2. LOUDS Tree erstellen + // std::cout << "[Init] Konvertiere zu LOUDS..." << std::endl; + // const LOUDSTree louds(baseline); + + // 3. Speichermessung + const uint64_t bytesBaseline = baseline.sizeInBytes(); + // uint64_t bytesLouds = louds.sizeInBytes(); + + std::cout << "Speicher Baseline: " << bytesBaseline / (1024.0 * 1024.0) << " MB" << std::endl; + // std::cout << "Speicher LOUDS: " << bytesLouds / (1024.0 * 1024.0) << " MB" << std::endl; + // if (bytesLouds > 0) { + // std::cout << "Faktor: " << (double) bytesBaseline / bytesLouds << "x" << std::endl; + // } + + // 4. Laufzeitmessung (Parent Operation) + // std::cout << "[Benchmark] Starte 1.000.000 Parent-Abfragen..." << std::endl; + // uint64_t checksum = 0; + + // auto start = std::chrono::high_resolution_clock::now(); + // for (uint64_t i = 2; i <= N; ++i) { + // checksum += louds.parent(i); + // } + // auto end = std::chrono::high_resolution_clock::now(); + + // auto duration = std::chrono::duration_cast(end - start).count(); + // std::cout << "Zeit LOUDS: " << duration << " ms" << std::endl; + // std::cout << "Checksum: " << checksum << std::endl; + + return 0; +}