github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/list/x_skl_rand.go (about) 1 package list 2 3 // References: 4 // https://gitee.com/bombel/cdf_skiplist 5 // http://snap.stanford.edu/data/index.html 6 // 7 // Goland math random (math.Float64()) contains global mutex lock 8 // Ref 9 // https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/math/rand/rand.go 10 // https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/math/bits/bits.go 11 // 1. Avoid using global mutex lock 12 // 2. Avoid generating random number each time 13 14 import ( 15 crand "crypto/rand" 16 "encoding/binary" 17 "math" 18 "math/bits" 19 randv2 "math/rand/v2" 20 ) 21 22 type SklRand func(maxLevel int, currentElements int64) int32 23 24 func maxLevels(totalElements int64, P float64) int { 25 // Ref https://www.cl.cam.ac.uk/teaching/2005/Algorithms/skiplists.pdf 26 // maxLevels = log(1/P) * log(totalElements) 27 // P = 1/4, totalElements = 2^32 - 1 28 if totalElements <= 0 { 29 return 0 30 } 31 return int(math.Ceil(math.Log(1/P) * math.Log(float64(totalElements)))) 32 } 33 34 func randomLevel(maxLevel int, currentElements int32) int32 { 35 level := 1 36 for float64(randv2.Int64()&0xFFFF) < sklProbability*0xFFFF { 37 level += 1 38 } 39 if level < sklMaxLevel { 40 return int32(level) 41 } 42 return sklMaxLevel 43 } 44 45 // randomLevelV2 is the skip list level element. 46 // Dynamic level calculation. 47 func randomLevelV2(maxLevel int, currentElements int64) int32 { 48 // Call function maxLevels to get total? 49 // maxLevel => n, 2^n-1, there will be 2^n-1 elements in the skip list 50 var total uint64 51 if maxLevel == sklMaxLevel { 52 total = uint64(math.MaxUint32) 53 } else { 54 total = uint64(1)<<maxLevel - 1 55 } 56 // Bits right shift equal to manipulate a high-level bit 57 // Calculate the minimum bits of the random number 58 // Lookup table. 59 level := maxLevel - bits.Len64(randv2.Uint64()&total) + 1 60 // Avoid the value of randomly generated level deviates 61 // far from the number of elements within the skip-list. 62 // Level should be greater than but approximate to log(currentElements) 63 for level > 3 && uint64(1)<<(level-3) > uint64(currentElements) { 64 level-- 65 } 66 return int32(level) 67 } 68 69 // randomLevelV3 is the skip list level element. 70 // Dynamic level calculation. 71 // Concurrency safe. 72 func randomLevelV3(maxLevel int, currentElements int64) int32 { 73 // Call function maxLevels to get total? 74 // maxLevel => n, 2^n-1, there will be 2^n-1 elements in the skip list 75 var total uint64 76 if maxLevel == sklMaxLevel { 77 total = uint64(math.MaxUint32) 78 } else { 79 total = uint64(1)<<maxLevel - 1 80 } 81 82 num := cryptoRandUint64() 83 rest := num & total 84 // Bits right shift equal to manipulate a high-level bit 85 // Calculate the minimum bits of the random number 86 tmp := bits.Len64(rest) // Lookup table. 87 level := maxLevel - tmp + 1 88 // Avoid the value of randomly generated level deviates 89 // far from the number of elements within the skip-list. 90 // level should be greater than but approximate to log(currentElements) 91 for level > 3 && uint64(1)<<(level-3) > uint64(currentElements) { 92 level-- 93 } 94 return int32(level) 95 } 96 97 func cryptoRandUint64() uint64 { 98 randUint64 := [8]byte{} 99 if _, err := crand.Read(randUint64[:]); err != nil { 100 panic(err) 101 } 102 if randUint64[7]&0x8 == 0x0 { 103 return binary.LittleEndian.Uint64(randUint64[:]) 104 } 105 return binary.BigEndian.Uint64(randUint64[:]) 106 } 107 108 func cryptoRandUint32() uint32 { 109 randUint32 := [4]byte{} 110 if _, err := crand.Read(randUint32[:]); err != nil { 111 panic(err) 112 } 113 if randUint32[3]&0x8 == 0x0 { 114 return binary.LittleEndian.Uint32(randUint32[:]) 115 } 116 return binary.BigEndian.Uint32(randUint32[:]) 117 }