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