From ca0e52bf670b063c7a2cfc307a86b4676862558f Mon Sep 17 00:00:00 2001 From: Martin Mares <mj@ucw.cz> Date: Wed, 20 Mar 2019 17:56:10 +0100 Subject: [PATCH] Fibonacci heap --- 05-fibonacci/cpp/Makefile | 12 ++ 05-fibonacci/cpp/fibonacci.h | 204 +++++++++++++++++++++ 05-fibonacci/cpp/fibonacci_test.cpp | 254 ++++++++++++++++++++++++++ 05-fibonacci/cpp/test_main.cpp | 43 +++++ 05-fibonacci/python/fibonacci.py | 165 +++++++++++++++++ 05-fibonacci/python/fibonacci_test.py | 221 ++++++++++++++++++++++ 05-fibonacci/task.md | 4 + 7 files changed, 903 insertions(+) create mode 100644 05-fibonacci/cpp/Makefile create mode 100644 05-fibonacci/cpp/fibonacci.h create mode 100644 05-fibonacci/cpp/fibonacci_test.cpp create mode 100644 05-fibonacci/cpp/test_main.cpp create mode 100644 05-fibonacci/python/fibonacci.py create mode 100644 05-fibonacci/python/fibonacci_test.py create mode 100644 05-fibonacci/task.md diff --git a/05-fibonacci/cpp/Makefile b/05-fibonacci/cpp/Makefile new file mode 100644 index 0000000..9cab32d --- /dev/null +++ b/05-fibonacci/cpp/Makefile @@ -0,0 +1,12 @@ +test: fibonacci_test + ./$< + +CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare + +fibonacci_test: fibonacci.h fibonacci_test.cpp test_main.cpp + $(CXX) $(CXXFLAGS) fibonacci_test.cpp test_main.cpp -o $@ + +clean:: + rm -f fibonacci_test + +.PHONY: clean test diff --git a/05-fibonacci/cpp/fibonacci.h b/05-fibonacci/cpp/fibonacci.h new file mode 100644 index 0000000..ddee892 --- /dev/null +++ b/05-fibonacci/cpp/fibonacci.h @@ -0,0 +1,204 @@ +#include <vector> + +// If the condition is not true, report an error and halt. +#define EXPECT(condition, message) do { if (!(condition)) expect_failed(message); } while (0) +void expect_failed(const std::string& message); + +/* + * payload_t: Type used to allow storing user data in each heap node. + * + * priority_t: Type used to represent priority of the node. It must + * support comparison. + * + * In proper C++, the heap should be a template parametrized by these + * types, but we decided to keep the code as simple as possible. + */ +typedef int payload_t; +typedef int priority_t; + +struct FibonacciHeap; + +// Node of FibonacciHeap +struct Node { + payload_t payload; + priority_t priority() { return prio; } + + private: + priority_t prio; + + Node *parent = nullptr, *first_child = nullptr; + + // Pointers in a cyclic list of children of the parent. + Node *prev_sibling, *next_sibling; + + int rank = 0; + + Node(priority_t p, payload_t payload) : payload(payload), prio(p) { + next_sibling = prev_sibling = this; + } + + ~Node() {} + + friend class FibonacciHeap; + friend class HeapTests; +}; + +/* FibonacciHeap + * + * Implemented using a meta root to avoid special handling of the + * linked list of heap trees. + */ +struct FibonacciHeap { + // Check whether the heap is empty + bool is_empty() { return size == 0; } + + /* Insert a new node with given priority and payload and return it. + * + * `payload` can be used to store any data in the node. + */ + Node *insert(priority_t prio, payload_t payload = payload_t()) { + Node *n = new Node(prio, payload); + add_child(&meta_root, n); + size++; + return n; + } + + /* Extract node with minimum priority. + * + * Must not be called when the heap is empty. + * Returns pair (payload, priority) of the removed node. + */ + std::pair<payload_t, priority_t> extract_min() { + EXPECT(meta_root.rank > 0, "Cannot extract minimum of an empty heap."); + + // Find the tree whose root is minimal. + Node *min = meta_root.first_child; + Node *node = min->next_sibling; + while (node != meta_root.first_child) { + if (node->prio < min->prio) min = node; + node = node->next_sibling; + } + + // Add all its subtrees to the heap. + while (min->rank > 0) + add_child(&meta_root, remove(min->first_child)); + + // Remove the root. + std::pair<payload_t, priority_t> ret = { min->payload, min->prio }; + delete remove(min); + size--; + + // Finally, consolidate the heap. + consolidate(); + + return ret; + } + + /* Decrease priority of node to new_prio. + * + * new_prio must not be higher than node.prio. + */ + void decrease(Node *node, priority_t new_prio) { + EXPECT(node, "Cannot decrase null pointer."); + EXPECT(node->prio >= new_prio, "Decrase: new priority larget than old one."); + // TODO: Implement + } + + FibonacciHeap() : size(0), meta_root(priority_t(), payload_t()) {} + + ~FibonacciHeap() { + if (size == 0) return; + Node *next; + Node *n = meta_root.first_child; + do { + while (n->first_child) n = n->first_child; + next = (n->next_sibling != n) ? n->next_sibling : n->parent; + delete remove(n); + } while ((n = next) != &meta_root); + } + + protected: + // Consolidate heap during extract_min. + void consolidate() { + std::vector<Node*> buckets(max_rank(), nullptr); + while (meta_root.first_child) { + Node *node = remove(meta_root.first_child); + while (Node *&b = buckets.at(node->rank)) { + node = pair_trees(b, node); + b = nullptr; + } + buckets.at(node->rank) = node; + } + + for (Node *node : buckets) + if (node) add_child(&meta_root, node); + } + + // Join two trees of the same rank. + Node *pair_trees(Node *a, Node *b) { + if (a->prio > b->prio) std::swap(a, b); + add_child(a, b); + return a; + } + + // Return maximum possible rank of a tree in a heap of the current size. + virtual size_t max_rank() { + size_t ret = 1; + while ((1 << ret) <= size) ret++; + return ret; + } + + // Check whether a node is the root. + bool is_root(Node *n) { + return n->parent == &meta_root; + } + + // Link together two elements of linked list -- a and b. + void join(Node *a, Node *b) { + a->next_sibling = b; + b->prev_sibling = a; + } + + // Add node as a child of a given parent. + virtual void add_child(Node* parent, Node *node) { + EXPECT(parent, "add_child: Parent cannot be nullptr."); + EXPECT(node, "add_child: Node cannot be nullptr."); + EXPECT(!node->parent, "add_child: Node already has a parent."); + EXPECT(parent == &meta_root || node->rank == parent->rank, + "add_child: Ranks of node and parent are different."); + EXPECT(node->prev_sibling == node && node->next_sibling == node, + "add_child: Node has a sibling."); + EXPECT(parent == &meta_root || parent->prio <= node->prio, + "add_child: Parent has bigger priority than node."); + + if (parent->rank == 0) parent->first_child = node; + else { + join(parent->first_child->prev_sibling, node); + join(node, parent->first_child); + } + + node->parent = parent; + parent->rank++; + } + + // Disconnect a node from its parent. + virtual Node *remove(Node *n) { + EXPECT(n->parent, "remove: Cannot disconnect node without parent."); + EXPECT(n != &meta_root, "remove: Cannot remove meta root."); + + n->parent->rank--; + if (n->parent->first_child == n) n->parent->first_child = n->next_sibling; + if (n->parent->rank == 0) n->parent->first_child = nullptr; + + n->parent = nullptr; + join(n->prev_sibling, n->next_sibling); + join(n, n); + + return n; + } + + friend class HeapTests; + + size_t size; + Node meta_root; +}; diff --git a/05-fibonacci/cpp/fibonacci_test.cpp b/05-fibonacci/cpp/fibonacci_test.cpp new file mode 100644 index 0000000..1a65079 --- /dev/null +++ b/05-fibonacci/cpp/fibonacci_test.cpp @@ -0,0 +1,254 @@ +#include <stdio.h> +#include <utility> +#include <functional> +#include <string> +#include <limits> +#include <algorithm> + +#include "fibonacci.h" + +struct HeapTests { + using Heap = FibonacciHeap; + + // Good enough infinity for our tests. + static const int infty = 1000*1000*1000; + + /* Iterator over all children of given node. + * Also implements begin() and end() so it can + * be plugged into for-each cycle. + */ + class NodeIterator { + Node *root; + Node *node; + bool recursive; + + public: + NodeIterator begin() { return *this; } + NodeIterator end() { return NodeIterator(); } + bool operator !=(const NodeIterator& b) { return node != b.node; } + + Node *operator*() { return node; } + NodeIterator &operator++() { + if (recursive && node->rank > 0) { + node = node->first_child; + } else { + while (node->next_sibling == node->parent->first_child) { + node = node->parent; + if (node == root) { + node = nullptr; + return *this; + } + } + node = node->next_sibling; + } + return *this; + } + + NodeIterator(Node *root = nullptr, bool recursive = true) : root(root), + node(root ? root->first_child : nullptr), recursive(recursive) {} + }; + + // Helper of show_heap. + static void show_node(Node *node, int indent) { + for (int i = 0; i < indent; i++) printf(" "); + printf("- %i (payload: %i)\n", node->priority(), node->payload); + for (Node* child : NodeIterator(node, false)) + show_node(child, indent + 1); + } + + // Show heap in human-readable form. + static void show_heap(Heap* H) { + printf("Heap of size %i\n", (int)H->size); + for (Node* root : NodeIterator(&H->meta_root, false)) + show_node(root, 0); + printf("\n"); + } + + // Check heap invariants. + static void audit_heap(Heap* H) { + int size = 0; + for (Node* node : NodeIterator(&H->meta_root)) { + size++; + EXPECT(H->is_root(node) || node->parent->priority() <= node->priority(), + "Parent has bigger priority than its child."); + std::vector<int> children; + for (Node *c : NodeIterator(node, false)) { + children.push_back(c->rank); + EXPECT(c->parent == node, "Inconsistent parent pointer."); + EXPECT(c->next_sibling->prev_sibling == c, "Inconsistent sibling pointers."); + EXPECT(c->prev_sibling->next_sibling == c, "Inconsistent sibling pointers."); + } + std::sort(children.begin(), children.end()); + EXPECT(children.size() == node->rank, + "Rank of node does not match its real number of children."); + for (int i = 0; i < children.size(); i++) + EXPECT(children[i] >= i - 1, "Child of the node has too small rank."); + } + EXPECT(size == H->size, "Size of the heap does not match real number of its nodes."); + } + + // Remove given node from heap. + static void remove(Heap* H, Node *n) { + H->decrease(n, -infty); + H->extract_min(); + } + + // Force consolidation of the heap. + static void consolidate(Heap* H) { + remove(H, H->insert(0)); + } + + /* Remove the subtree rooted in node from the heap. + * + * Note that this method slightly cheats because it + * learns what nodes are in the subtree by walking over it. + */ + static void remove_subtree(Heap* H, Node *node) { + std::vector<Node*> to_remove; + + for (Node* n : NodeIterator(node)) to_remove.push_back(n); + to_remove.push_back(node); + + for (Node* n : to_remove) remove(H, n); + } + + /* Build a tree of a given rank with as few nodes as possible. + * + * Return pair (root of the tree, its child with the largest rank). + */ + static std::pair<Node*,Node*> build_sparse_tree(Heap *H, int rank, int prio) { + if (rank == 0) return { H->insert(prio), nullptr }; + + auto A = build_sparse_tree(H, rank - 1, prio); + auto B = build_sparse_tree(H, rank - 1, prio + 1); + + consolidate(H); + if (B.second) remove_subtree(H, B.second); + + return { A.first, B.first }; + } + + // Check whether H contains exacly one path and nothing else. + static void check_path(Heap* H) { + Node *node = &H->meta_root; + while (node->rank == 1) node = node->first_child; + EXPECT(node->rank == 0, "Expected path but found node of rank larger than 1."); + } + + /* Build a path of a given length. + * + * Return lowest node on this path. All nodes on path are marked. + */ + static Node* build_path(Heap *H, int length) { + Node *a, *b; + std::vector<Node*> to_remove; + H->insert(length + 1); + Node *end = H->insert(length + 2); + consolidate(H); + to_remove.push_back(H->insert(length + 2)); + a = H->insert(length + 3); + consolidate(H); + remove(H, a); + + for (int i = length; i > 0; i--) { + H->insert(i); + to_remove.push_back(H->insert(i + 1)); + consolidate(H); + a = H->insert(i + 1); + b = H->insert(i + 2); + consolidate(H); + remove(H, b); + remove(H, a); + } + + for (Node *n : to_remove) remove(H, n); + + check_path(H); + + return end; + } + + /* A simple random test. + * + * It does in order: + * - randomly insert elements, + * - do random decreases, + * - extract_min until the heap is empty. + */ + static void test_random(int size, bool print) { + Heap H; + std::vector<std::pair<Node*, int>> node_map; + + for (int i = 0; i < size; i++) { + int prio = (1009 * i) % 2099; + node_map.push_back({ H.insert(prio, i), prio }); + } + + consolidate(&H); + if (print) show_heap(&H); + + for (int i = 0; i < size; i++) { + if (i % (size / 10) == 0) audit_heap(&H); + int idx = (i * 839) % node_map.size(); + auto& X = node_map[idx]; + EXPECT(X.first->priority() == X.second, + "Priority of node changed but its was not decreased."); + int new_prio = X.second - ((i * 131 + 15) % 239); + H.decrease(X.first, new_prio); + EXPECT(X.first->priority() == new_prio, + "Decrease failed to change priority of the node."); + if (print) { + printf("Decrease %i -> %i (payload %i)\n", X.second, new_prio, X.first->payload); + show_heap(&H); + } + X.second = new_prio; + } + + int last = std::numeric_limits<int>::min(); + audit_heap(&H); + while (!H.is_empty()) { + auto x = H.extract_min(); + payload_t payload = x.first; + priority_t prio = x.second; + EXPECT(last <= prio, "extract_min is not monotone."); + EXPECT(node_map[payload].first, + "Extracted node was already deleted or something changed its payload."); + EXPECT(prio == node_map[payload].second, + "Priority of extracted node is wrong."); + last = prio; + node_map[payload].first = nullptr; + if (print) { + printf("Extract min %i (payload %i)\n", prio, payload); + show_heap(&H); + } + } + + for (auto& X : node_map) EXPECT(X.first == nullptr, "Not all nodes were deleted."); + } + + // Build a sparse tree of given rank and then empty the heap. + static void test_sparse_tree(int rank) { + Heap H; + build_sparse_tree(&H, rank, 1); + audit_heap(&H); + while (!H.is_empty()) H.extract_min(); + } + + // Build a path, decrease it lowest node, and empty the heap. + static void test_path(int length) { + Heap H; + H.decrease(build_path(&H, length), -infty); + for (Node *n : NodeIterator(&H.meta_root)) + EXPECT(n->rank == 0, "Decrease of lowest node should have broken " + "the path into forest of isolated vertices but it has not."); + audit_heap(&H); + while (!H.is_empty()) H.extract_min(); + } +}; + +std::vector<std::pair<std::string, std::function<void()>>> tests = { + { "small", [] { HeapTests::test_random(11, true); } }, + { "random", [] { HeapTests::test_random(10000, false); } }, + { "sparse", [] { HeapTests::test_sparse_tree(16); } }, + { "path", [] { HeapTests::test_path(5000); } }, +}; diff --git a/05-fibonacci/cpp/test_main.cpp b/05-fibonacci/cpp/test_main.cpp new file mode 100644 index 0000000..3f4aff0 --- /dev/null +++ b/05-fibonacci/cpp/test_main.cpp @@ -0,0 +1,43 @@ +#include <cstdlib> +#include <functional> +#include <iostream> +#include <string> +#include <utility> +#include <vector> + +using namespace std; + +extern vector<pair<string, function<void()>>> tests; + +void expect_failed(const string& message) { + cerr << "Test error: " << message << endl; + exit(1); +} + +int main(int argc, char* argv[]) { + vector<string> required_tests; + + if (argc > 1) { + required_tests.assign(argv + 1, argv + argc); + } else { + for (const auto& test : tests) + required_tests.push_back(test.first); + } + + for (const auto& required_test : required_tests) { + bool found = false; + for (const auto& test : tests) + if (required_test == test.first) { + cerr << "Running test " << required_test << endl; + test.second(); + found = true; + break; + } + if (!found) { + cerr << "Unknown test " << required_test << endl; + return 1; + } + } + + return 0; +} diff --git a/05-fibonacci/python/fibonacci.py b/05-fibonacci/python/fibonacci.py new file mode 100644 index 0000000..e05c156 --- /dev/null +++ b/05-fibonacci/python/fibonacci.py @@ -0,0 +1,165 @@ +class Node: + """Node of `FibonacciHeap`. + + Property `payload` can be used to store any information + the user needs. + """ + + def __init__(self, prio, payload = None): + self.payload = payload + + self._prio = prio + self._parent = None + + self._rank = 0 + self._first_child = None + + # Pointers in a cyclic list of children of the parent. + self._prev_sibling = self + self._next_sibling = self + + def priority(self): + """Return priority of the node.""" + return self._prio + +class FibonacciHeap: + """Fibonacci heap. + + Implemented using a meta root to avoid special handling of the + linked list of heap trees. + """ + def __init__(self): + self._size = 0 + self._meta_root = Node(None) + + def is_empty(self): + """Check whether the heap is empty.""" + return self._size == 0 + + def insert(self, prio, payload = None): + """Insert a new node with given priority and payload and return it. + + `payload` can be used to store any data in the node. + """ + n = Node(prio, payload) + self._add_child(self._meta_root, n) + self._size += 1 + return n + + def extract_min(self): + """Extract node with minimum priority. + + Must not be called when the heap is empty. + Returns tuple (payload, priority) of the removed node. + """ + assert self._size > 0, "Cannot extract minimum of an empty heap." + + # Find the tree whose root is minimal. + minimum = self._meta_root._first_child + node = self._meta_root._first_child._next_sibling + while node is not self._meta_root._first_child: + if node._prio < minimum._prio: + minimum = node + node = node._next_sibling + + self._remove(minimum) + self._size -= 1 + + # Add all its subtrees to the heap. + while minimum._rank > 0: + self._add_child(self._meta_root, self._remove(minimum._first_child)) + + # Finally, consolidate the heap. + self._consolidate() + + return (minimum.payload, minimum._prio) + + def _consolidate(self): + """INTERNAL: Consolidate heap during extract_min. + """ + buckets = [ None ] * self._max_rank() + while self._meta_root._rank > 0: + node = self._remove(self._meta_root._first_child) + while buckets[node._rank] is not None: + b = node._rank + node = self._pair_trees(node, buckets[b]) + buckets[b] = None + buckets[node._rank] = node + + for node in buckets: + if node is not None: + self._add_child(self._meta_root, node) + + def _pair_trees(self, a, b): + """INTERNAL: Join two trees of the same rank. + """ + if a._prio > b._prio: a, b = b, a + self._add_child(a, b) + return a + + def _max_rank(self): + """INTERNAL: Return maximum possible rank of a tree in a heap of the current size. + """ + ret = 1 + while (1 << ret) <= self._size: ret += 1 + return ret + + def decrease(self, node, new_prio): + """Decrease priority of node to new_prio. + + new_prio must not be higher than node._prio. + """ + assert node is not None, "Cannot decrease None." + assert node is not self._meta_root, "Cannot decrease meta root." + assert new_prio <= node._prio, "Decrease: new priority is bigger than old one." + # TODO: Implement + raise NotImplementedError + + def _is_root(self, node): + """Check wheter node is root.""" + return node._parent is self._meta_root + + def _add_child(self, parent, node): + """INTERNAL: Add node as child of parent. + """ + assert parent is not None, "_add_child: Parent cannot be None." + assert node is not None, "_add_child: Cannot add None as a child." + assert node._parent is None, "_add_child: node already has a parent." + assert parent is self._meta_root or node._rank == parent._rank, \ + "_add_child: ranks of node and parent are different" + assert node._prev_sibling is node and node._next_sibling is node, \ + "_add_child: node has a sibling" + assert parent is self._meta_root or parent._prio <= node._prio, \ + "_add_child: parent has bigger priority than node" + + if parent._rank == 0: + parent._first_child = node + else: + self._join(parent._first_child._prev_sibling, node) + self._join(node, parent._first_child) + + node._parent = parent + parent._rank += 1 + + def _remove(self, node): + """INTERNAL: Disconect node from parent. + """ + assert node._parent is not None, "_remove: Cannot disconnect node without parent." + assert node is not self._meta_root, "_remove: Cannot remove meta root." + + node._parent._rank -= 1 + if node._parent._rank == 0: + node._parent._first_child = None + elif node._parent._first_child is node: + node._parent._first_child = node._next_sibling + + node._parent = None + self._join(node._prev_sibling, node._next_sibling) + self._join(node, node) + return node + + def _join(self, a, b): + """INTERNAL: Link together two elements of linked list -- a and b. + """ + a._next_sibling = b + b._prev_sibling = a diff --git a/05-fibonacci/python/fibonacci_test.py b/05-fibonacci/python/fibonacci_test.py new file mode 100644 index 0000000..aeaf8bb --- /dev/null +++ b/05-fibonacci/python/fibonacci_test.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 + +from fibonacci import FibonacciHeap as Heap +from math import log + +def children(root, recursive = True): + """Iterate over all children of `root`. + """ + if root._first_child is None: return + + node = root._first_child + while True: + yield node + if recursive and node._rank > 0: + node = node._first_child + else: + while node._next_sibling is node._parent._first_child: + node = node._parent + if node is root: return + node = node._next_sibling + +def show_node(node, indent): + """Helper of show_heap. + """ + print("%s- %s (payload: %s)" % (" " * indent, node.priority(), node.payload)) + for child in children(node, False): + show_node(child, indent + 1) + +def show_heap(heap): + """Show heap in human-readable form. + """ + print("Heap of size %i" % heap._size) + for root in children(heap._meta_root, False): + show_node(root, 0) + print("") + +def audit_heap(heap): + """Check various heap invariants. + """ + assert heap._meta_root is not None, "Meta root is None" + assert heap._size == sum( 1 for _ in children(heap._meta_root) ), \ + "Size of the heap does not match real number of its nodes." + + for root in children(heap._meta_root): + assert heap._is_root(root) or root._parent.priority() <= root.priority(), \ + "Parent has bigger priority than its child." + + child_sizes = [] + for c in children(root, False): + child_sizes.append(c._rank) + assert c._parent is root, "Inconsistent parent pointer." + assert c._next_sibling._prev_sibling is c, "Inconsistent sibling pointers." + assert c._prev_sibling._next_sibling is c, "Inconsistent sibling pointers." + + assert len(child_sizes) == root._rank, \ + "Rank of node does not match its real number of children." + for i, s in enumerate(sorted(child_sizes)): + assert s >= i - 1, "Child of the node has too small rank." + + +def remove(H, node): + """Remove node from heap H. + """ + H.decrease(node, -10**9) + H.extract_min() + +def consolidate(H): + """Force consolidation of heap H. + """ + remove(H, H.insert(0)) + +def remove_subtree(H, node): + """Remove subtree rooted in node from the heap. + + Note that this method slightly cheats because it + learns what nodes are in the subtree by walking over it. + """ + to_remove = list(children(node)) + to_remove.append(node) + + for node in to_remove: remove(H, node) + +def build_sparse_tree(H, rank, prio): + """Build a tree of a given rank with as few nodes as possible. + + Return pair (root of the tree, its child with largest rank). + """ + if rank == 0: + return (H.insert(prio), None) + + a, _ = build_sparse_tree(H, rank - 1, prio) + b, to_remove = build_sparse_tree(H, rank - 1, prio + 1) + + consolidate(H) + if to_remove is not None: + remove_subtree(H, to_remove) + + return (a, b) + +def check_path(H): + """Check whether H contains exactly one path and nothing else. + """ + node = H._meta_root + while node._rank == 1: node = node._first_child + assert node._rank == 0, "Expected path but found node of rank larger than 1." + +def build_path(H, length): + """Build a path of given length. + + Return lowest node on this path. All nodes on path are marked. + """ + to_remove = [] + H.insert(length + 1) + end = H.insert(length + 2) + consolidate(H) + to_remove.append(H.insert(length + 2)) + a = H.insert(length + 3) + consolidate(H) + remove(H, a) + + for i in range(length, 0, -1): + H.insert(i) + to_remove.append(H.insert(i + 1)) + consolidate(H) + a = H.insert(i + 1) + b = H.insert(i + 2) + consolidate(H) + remove(H, b) + remove(H, a) + + for n in to_remove: + remove(H, n) + + check_path(H) + + return end + +def test_random(size, do_print): + """A simple random test. + + It does in order: + - randomly insert elements, + - do random decreases, + - extract_min until the heap is empty. + """ + H = Heap() + node_map = [] + + for i in range(size): + prio = (1009 * i) % 2099 + node_map.append([H.insert(prio, payload=i), prio]) + + consolidate(H) + if do_print: show_heap(H) + + for i in range(size): + if i % (size // 10) == 0: + audit_heap(H) + idx = (i * 839) % len(node_map) + n, prio = node_map[idx] + assert n.priority() == prio, "Priority of node changed but its was not decreased." + new_prio = prio - ((i * 131 + 15) % 239) + H.decrease(n, new_prio) + assert n.priority() == new_prio, "Decrease failed to change priority of the node." + node_map[idx][1] = new_prio + if do_print: + print("Decrease %s -> %s (payload %s)" % (prio, new_prio, n.payload)) + show_heap(H) + + last = None + audit_heap(H) + while not H.is_empty(): + payload, prio = H.extract_min() + assert last is None or prio >= last, "extract_min is not monotone." + assert node_map[payload][0], \ + "Extracted node was already deleted or something changed its payload." + assert prio == node_map[payload][1], "Priority of extracted node is wrong." + last = prio + node_map[payload] = None + if do_print: + print("Extract min %s (payload %s)" % (prio, payload)) + show_heap(H) + + assert all( x is None for x in node_map ), "Not all nodes were deleted." + +def test_sparse_tree(rank): + """Build a sparse tree of given rank and then empty the heap.""" + H = Heap() + build_sparse_tree(H, rank, 1) + audit_heap(H) + while not H.is_empty(): + H.extract_min() + +def test_path(length): + """Build a path, decrease it lowest node, and empty the heap.""" + H = Heap() + H.decrease(build_path(H, length), -1000) + assert all( n._rank == 0 for n in children(H._meta_root, False) ), \ + "Decrease of lowest node should have broken the path into forest " + \ + "of isolated vertices but it did not." + audit_heap(H) + while not H.is_empty(): + H.extract_min() + +tests = [ + ("small", lambda: test_random(11, True)), + ("random", lambda: test_random(10000, False)), + ("sparse", lambda: test_sparse_tree(16)), + ("path", lambda: test_path(5000)), +] + +if __name__ == "__main__": + import sys + for required_test in sys.argv[1:] or [name for name, _ in tests]: + for name, test in tests: + if name == required_test: + print("Running test {}".format(name), file=sys.stderr) + test() + break + else: + raise ValueError("Unknown test {}".format(name)) diff --git a/05-fibonacci/task.md b/05-fibonacci/task.md new file mode 100644 index 0000000..2e04802 --- /dev/null +++ b/05-fibonacci/task.md @@ -0,0 +1,4 @@ +You are given an implementation of a lazy binomial heap, which supports +Insert and ExtractMin operations. + +Turn it into a Fibonacci heap and implement Decrease. -- GitLab