added new item tower height generator for skiplist

This commit is contained in:
Dominik Tomičević 2015-06-23 15:17:10 +02:00
parent 7c0e34aac9
commit adc80bf29a
4 changed files with 174 additions and 12 deletions

View File

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

View File

@ -0,0 +1,82 @@
#ifndef MEMGRAPH_DATA_STRUCTURES_SKIPLIST_HPP
#define MEMGRAPH_DATA_STRUCTURES_SKIPLIST_HPP
#include <algorithm>
#include <cstdlib>
#include <array>
#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 <class K, class T>
struct SkipNode
{
SkipNode(K* key, T* item)
: key(key), item(item) {}
K* key;
T* item;
SkipNode* up;
SkipNode* forward;
};
template <class K,
class T,
size_t height = 16>
class SkipList
{
using Node = SkipNode<K, T>;
using Tower = std::array<Node, height>;
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<T, K>(key, item);
Tower trace;
size_t h = height - 1;
while(h--)
{
}
}
void del(const K* const key)
{
}
private:
Tower head;
};
#endif

44
test/skiplist.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <iostream>
#include <array>
#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<int, max_height> 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);
}
}

View File

@ -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<unsigned long long> 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;
}
}