Task 10 exercise a

This commit is contained in:
Jan-Niclas Loosen
2026-01-26 22:40:07 +01:00
parent 90ce78f17d
commit b8a2972b6b
5 changed files with 392 additions and 1 deletions

View File

@@ -17,7 +17,7 @@ dice_props = [0.1, 0.1, 0.1, 0.1, 0.1, 0.5]
coin_sides = ['Head', 'Number'] coin_sides = ['Head', 'Number']
coin_head = [0.1, 0.5] coin_head = [0.1, 0.5]
rep = 100000 rep = 100
# --- Dice test --- # --- Dice test ---
res_cpp, acc_dice, rej_dice = throw_dice_cpp(rep, dice_sides, dice_props) res_cpp, acc_dice, rej_dice = throw_dice_cpp(rep, dice_sides, dice_props)

59
Uebung-03/find.py Normal file
View File

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

126
Uebung-10/BitVector.hpp Normal file
View File

@@ -0,0 +1,126 @@
#pragma once
#include <vector>
#include <cstdint>
#include <stdexcept>
#include <algorithm>
class BitVector {
std::vector<uint64_t> data;
std::vector<uint32_t> 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; }
};

BIN
Uebung-10/main Executable file

Binary file not shown.

206
Uebung-10/main.cpp Normal file
View File

@@ -0,0 +1,206 @@
#include <vector>
#include <queue>
#include <cstdint>
#include <random>
#include <stdexcept>
#include <iostream>
#include "BitVector.hpp"
// ==========================================
// Aufgabenteil a) Baseline-Implementierung
// ==========================================
class BaselineTree {
public:
struct Node {
uint32_t parent = 0; // 0 = Root
std::vector<uint32_t> children;
};
private:
std::vector<Node> 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<uint32_t>& 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<typename Fn>
void bfs(Fn&& fn) const {
std::queue<uint32_t> 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<uint32_t> 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<std::chrono::milliseconds>(end - start).count();
// std::cout << "Zeit LOUDS: " << duration << " ms" << std::endl;
// std::cout << "Checksum: " << checksum << std::endl;
return 0;
}