1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
//! A skiplist is a list imlementation such that elements are always sorted, and the //! insertion and deletion of elements are done in `O(log(n))`. //! //! Conceptually, a skiplist resembles something like: //! //! ```text //! <head> ----------> [2] --------------------------------------------------> [9] ----------> //! <head> ----------> [2] ------------------------------------[7] ----------> [9] ----------> //! <head> ----------> [2] ----------> [4] ------------------> [7] ----------> [9] --> [10] -> //! <head> --> [1] --> [2] --> [3] --> [4] --> [5] --> [6] --> [7] --> [8] --> [9] --> [10] -> //! ``` //! //! where we see that a node `[x]` will have `n` pointers to other nodes, where `n` represents //! which level that node reaches. The idea is that when a node needs to be found (say for //! insertion), then you start at the highest level and move down. This allows for many of the //! lower-level nodes to be skipped thus making it faster. //! //! Each skiplist has an associated sorting function. By default, the sorting function is //! `|a, b| a.cmp(b)`, but any function is permitted, so long as it satisfies the following properties: //! - It must be consistent: `compare(a, b)` should always return the same result; //! - It should be anti-symmetric: If `compare(a, b) == Less`, then `compare(a, b) == Greater`. //! If this is not true, then some unexpected (and probably unsafe) behaviour may happen. //! //! Due to the nature of the skiplist being always sorted, it is not possible to get mutable //! pointers to the elements of the skiplist as this could leave the skiplist in an inconsistent //! state. As a result, the `IndexMut` traits, `iter_mut`, and `get_mut` methods and traits are //! not implemented. extern crate rand; use rand::distributions::{self, Sample}; //////////////////////////////////////////////////////////////////////////////////////////////////// // Level Generator //////////////////////////////////////////////////////////////////////////////////////////////////// /// Upon the insertion of a new node in the list, the node is replicated to high levels with a /// certain probability as determined by a `LevelGenerator`. /// /// The `total()` reflects the total number of levels, and `random()` should produce an integer in /// the range `[0, total)` with the desired probability distribution. /// /// The most commonly used probability distribution is a geometrical distribution, whereby the /// chance that a node occupies level `n` is `p` times as likely as occupying level `n-1`. /// Typically, `p` is equal to 1/2, though over values can be used which will trade speed against /// memory. /// /// This trait is strictly speaking not necessary (hence not public), but if in the future there is /// a need to allow for custom level generators then this trait is ready to go and it will just be /// a matter of modifying the skiplist implementation. pub trait LevelGenerator { fn random(&mut self) -> usize; fn total(&self) -> usize; } /// A level generator which will produce geometrically distributed numbers. pub struct GeometricalLevelGenerator { total: usize, p: f64, unit_range: distributions::Range<f64>, rng: rand::XorShiftRng // Fast generator } impl GeometricalLevelGenerator { /// Create a new GeometricalLevelGenerator with `total` number of levels, and `p` as the /// probability that a given node is present in the next level. /// /// # Panics /// /// `p` must be between 0 and 1 and will panic otherwise. Similarly, `total` must be at /// greater or equal to 1. pub fn new(total: usize, p: f64) -> Self { if total == 0 { panic!("total must be non-zero."); } if p <= 0.0 || p >= 1.0 { panic!("p must be in (0, 1)."); } GeometricalLevelGenerator { total: total, p: p, unit_range: distributions::Range::new(0.0f64, 1.0), rng: rand::XorShiftRng::new_unseeded() } } } impl LevelGenerator for GeometricalLevelGenerator { fn random(&mut self) -> usize { let mut h = 0; let mut x = self.p; let f = 1.0 - self.unit_range.sample(&mut self.rng); while x > f && h + 1 < self.total { h += 1; x *= self.p } h } fn total(&self) -> usize { self.total } }