Task 10 exercise a
This commit is contained in:
@@ -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
59
Uebung-03/find.py
Normal 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
126
Uebung-10/BitVector.hpp
Normal 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
BIN
Uebung-10/main
Executable file
Binary file not shown.
206
Uebung-10/main.cpp
Normal file
206
Uebung-10/main.cpp
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user