diff --git a/data_structures/skiplist/new_height.hpp b/data_structures/skiplist/new_height.hpp new file mode 100644 index 000000000..eec75e9f9 --- /dev/null +++ b/data_structures/skiplist/new_height.hpp @@ -0,0 +1,24 @@ +#ifndef MEMGRAPH_DATA_STRUCTURES_SKIPLIST_NEW_HEIGHT_HPP +#define MEMGRAPH_DATA_STRUCTURES_SKIPLIST_NEW_HEIGHT_HPP + +#include "utils/random/xorshift.hpp" + +size_t new_height(int max_height) +{ + // get 64 random bits (coin tosses) + uint64_t rand = xorshift::next(); + size_t height = 0; + + // for every head (1) increase the tower height by one until the tail (0) + // comes. this gives the following probabilities for tower heights: + // + // 1/2 1/4 1/8 1/16 1/32 1/64 ... + // 1 2 3 4 5 6 ... + // + while(max_height-- && ((rand >>= 1) & 1)) + height++; + + return height; +} + +#endif diff --git a/data_structures/skiplist/skiplist.hpp b/data_structures/skiplist/skiplist.hpp new file mode 100644 index 000000000..aa6ece728 --- /dev/null +++ b/data_structures/skiplist/skiplist.hpp @@ -0,0 +1,82 @@ +#ifndef MEMGRAPH_DATA_STRUCTURES_SKIPLIST_HPP +#define MEMGRAPH_DATA_STRUCTURES_SKIPLIST_HPP + +#include +#include +#include + +#include "utils/random/xorshift.hpp" + +size_t new_height(int max_height) +{ + uint64_t rand = xorshift::next(); + size_t height = 0; + + while(max_height-- && (rand >>= 1) & 1) + height++; + + return height; +} + + +template +struct SkipNode +{ + SkipNode(K* key, T* item) + : key(key), item(item) {} + + K* key; + T* item; + + SkipNode* up; + SkipNode* forward; +}; + +template +class SkipList +{ + using Node = SkipNode; + using Tower = std::array; + +public: + SkipList() + { + head.fill(nullptr); + } + + T* get(const K* const key) + { + size_t h = height; + + while(h--) + { + + } + } + + void put(const K* key, T* item) + { + auto* node = new SkipNode(key, item); + + Tower trace; + + size_t h = height - 1; + + while(h--) + { + + } + } + + void del(const K* const key) + { + + } + +private: + Tower head; +}; + +#endif diff --git a/test/skiplist.cpp b/test/skiplist.cpp new file mode 100644 index 000000000..ea8a185a4 --- /dev/null +++ b/test/skiplist.cpp @@ -0,0 +1,44 @@ +#include +#include + +#include "catch.hpp" + +#include "data_structures/skiplist/new_height.hpp" + +TEST_CASE("New height distribution must be approx. 1/2 1/4 1/8 ...") +{ + // NEVER forget to do this. + xorshift::init(); + + constexpr int N = 1e8; + constexpr int max_height = 8; + + // 2% is a good margin to start with, no? + // depends on the max_height and N, beware. + constexpr double error_margin = 0.02; + + // array to store the number of i-height towers generated + std::array heights; + heights.fill(0); + + // generate a tower and put it in a box with his same-height brothers + for(int i = 0; i < N; ++i) + heights[new_height(max_height)]++; + + // evaluate the number of towers in all of the boxes + for(int i = 0; i < max_height; ++i) + { + // compute how much towers should be in this box + int x = N / (2 << i); + + // the actual number of towers + int xx = heights[i]; + + // relative error + double relative_error = (double)std::abs(xx - x) / xx; + + // this might fail actually in some cases, especially if N is not big + // enough. it's probabilistic after all :D + REQUIRE(relative_error < error_margin); + } +} diff --git a/utils/random/xorshift.hpp b/utils/random/xorshift.hpp index bbdb7ab16..0abd18114 100644 --- a/utils/random/xorshift.hpp +++ b/utils/random/xorshift.hpp @@ -6,7 +6,19 @@ namespace xorshift { - static uint64_t x, y, z; + static uint64_t s[2]; + + uint64_t avalance(uint64_t s) + { + // MurmurHash3 finalizer + s ^= s >> 33; + s *= 0xff51afd7ed558ccd; + s ^= s >> 33; + s *= 0xc4ceb9fe1a85ec53; + s ^= s >> 33; + + return s; + } void init() { @@ -16,23 +28,23 @@ namespace xorshift std::mt19937_64 gen(rd()); std::uniform_int_distribution dist; - x = dist(gen); - y = dist(gen); - z = dist(gen); + // the number generated by MT can be full of zeros and xorshift + // doesn't like this so we use MurmurHash3 64bit finalizer to + // make it less biased + + s[0] = avalance(dist(gen)); + s[1] = avalance(dist(gen)); } uint64_t next() { - // period 2^96 - 1 - uint64_t t; + uint64_t s1 = s[0]; + const uint64_t s0 = s[1]; - x ^= x << 16; - x ^= x >> 5; - x ^= x << 1; + s[0] = s0; + s1 ^= s1 << 23; - t = x, x = y, y = z; - - return t ^ x ^ y; + return (s[1] = (s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26))) + s0; } }