github.com/benz9527/toy-box/algo@v0.0.0-20240221120937-66c0c6bd5abd/list/skip_list_intf.go (about)

     1  package list
     2  
     3  import (
     4  	"math"
     5  	"math/bits"
     6  )
     7  
     8  // Ref
     9  // paper:
    10  // https://www.cl.cam.ac.uk/teaching/2005/Algorithms/skiplists.pdf
    11  // github:
    12  // classic: https://github.com/antirez/disque/blob/master/src/skiplist.h
    13  // classic: https://github.com/antirez/disque/blob/master/src/skiplist.c
    14  // https://github.com/liyue201/gostl
    15  // https://github.com/chen3feng/stl4go
    16  // test:
    17  // https://github.com/chen3feng/skiplist-survey
    18  
    19  const (
    20  	ClassicSkipListMaxLevel    = 32   // 2^32 - 1 elements
    21  	ClassicSkipListProbability = 0.25 // P = 1/4, a skip list node element has 1/4 probability to have a level
    22  )
    23  
    24  type SkipListNodeElement[E comparable] interface {
    25  	GetObject() E
    26  	GetVerticalBackward() SkipListNodeElement[E]
    27  	SetVerticalBackward(backward SkipListNodeElement[E])
    28  	GetLevels() []SkipListLevel[E]
    29  	Free()
    30  }
    31  
    32  type SkipListLevel[E comparable] interface {
    33  	GetSpan() int64
    34  	SetSpan(span int64)
    35  	GetHorizontalForward() SkipListNodeElement[E]
    36  	SetHorizontalForward(forward SkipListNodeElement[E])
    37  }
    38  
    39  type SkipList[E comparable] interface {
    40  	GetLevel() int
    41  	Len() int64
    42  	Insert(v E) SkipListNodeElement[E]
    43  	Remove(v E) SkipListNodeElement[E]
    44  	Find(v E) SkipListNodeElement[E]
    45  	PopHead() E
    46  	PopTail() E
    47  	Free()
    48  	ForEach(fn func(idx int64, v E))
    49  }
    50  
    51  // LessThan is the compare function.
    52  type compareTo[E comparable] func(a, b E) int
    53  
    54  // SkipListRandomLevel is the skip list level element.
    55  // Dynamic level calculation.
    56  func SkipListRandomLevel(random func() uint64, maxLevel int) int {
    57  	// goland math random (math.Float64()) contains global mutex lock
    58  	// Ref
    59  	// https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/math/rand/rand.go
    60  	// https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/math/bits/bits.go
    61  	// 1. Avoid to use global mutex lock
    62  	// 2. Avoid to generate random number each time
    63  	total := uint64(1)<<maxLevel - 1 // maxLevel => n, 2^n -1, there will be 2^n-1 elements in the skip list
    64  	rest := random() % total
    65  	// Bits right shift equals to manipulate a high level bit
    66  	// Calculate the minimum bits of the random number
    67  	level := maxLevel - bits.Len64(rest) + 1
    68  	return level
    69  }
    70  
    71  func MaxLevels(totalElements int64, P float64) int {
    72  	// Ref https://www.cl.cam.ac.uk/teaching/2005/Algorithms/skiplists.pdf
    73  	// MaxLevels = log(1/P) * log(totalElements)
    74  	// P = 1/4, totalElements = 2^32 - 1
    75  	return int(math.Ceil(math.Log(1/P) * math.Log(float64(totalElements))))
    76  }