github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zstring/rand.go (about) 1 package zstring 2 3 import ( 4 "crypto/rand" 5 "encoding/hex" 6 "errors" 7 "io" 8 "sort" 9 "strconv" 10 "time" 11 ) 12 13 // RandUint32Max returns pseudorandom uint32 in the range [0..max) 14 func RandUint32Max(max uint32) uint32 { 15 x := RandUint32() 16 return uint32((uint64(x) * uint64(max)) >> 32) 17 } 18 19 // RandInt random numbers in the specified range 20 func RandInt(min int, max int) int { 21 if max < min { 22 max = min 23 } 24 return min + int(RandUint32Max(uint32(max+1-min))) 25 } 26 27 // Rand random string of specified length, the second parameter limit can only appear the specified character 28 func Rand(n int, tpl ...string) string { 29 var s string 30 b := make([]byte, n) 31 if len(tpl) > 0 { 32 s = tpl[0] 33 } else { 34 s = letterBytes 35 } 36 l := len(s) - 1 37 for i := n - 1; i >= 0; i-- { 38 idx := RandInt(0, l) 39 b[i] = s[idx] 40 } 41 return Bytes2String(b) 42 } 43 44 // UniqueID unique id minimum 6 digits 45 func UniqueID(n int) string { 46 if n < 6 { 47 n = 6 48 } 49 k := make([]byte, n) 50 if _, err := io.ReadFull(rand.Reader, k); err != nil { 51 return Rand(n-2) + strconv.Itoa(time.Now().Nanosecond()/10000000) 52 } 53 return hex.EncodeToString(k) 54 } 55 56 type ( 57 Weighteder struct { 58 choices []interface{} 59 totals []uint32 60 max uint32 61 } 62 choice struct { 63 Item interface{} 64 Weight uint32 65 } 66 ) 67 68 func WeightedRand(choices map[interface{}]uint32) (interface{}, error) { 69 w, err := NewWeightedRand(choices) 70 if err != nil { 71 return nil, err 72 } 73 return w.Pick(), nil 74 } 75 76 func NewWeightedRand(choices map[interface{}]uint32) (*Weighteder, error) { 77 cs := make([]choice, 0, len(choices)) 78 for k, v := range choices { 79 cs = append(cs, choice{Item: k, Weight: v}) 80 } 81 82 sort.Slice(cs, func(i, j int) bool { 83 return cs[i].Weight < cs[j].Weight 84 }) 85 w := &Weighteder{ 86 totals: make([]uint32, len(choices)), 87 choices: make([]interface{}, len(choices)), 88 max: 0, 89 } 90 for i := range cs { 91 if cs[i].Weight < 0 { 92 continue // ignore negative weights, can never be picked 93 } 94 95 if cs[i].Weight >= ^uint32(0) { 96 return nil, errors.New("weight overflowed") 97 } 98 99 if (^uint32(0) - w.max) <= cs[i].Weight { 100 return nil, errors.New("total weight overflowed") 101 } 102 103 w.max += cs[i].Weight 104 w.totals[i] = w.max 105 w.choices[i] = cs[i].Item 106 } 107 108 return w, nil 109 } 110 111 func (w *Weighteder) Pick() interface{} { 112 return w.choices[w.weightedSearch()] 113 } 114 115 func (w *Weighteder) weightedSearch() int { 116 x := RandUint32Max(w.max) + 1 117 i, j := 0, len(w.totals) 118 for i < j { 119 h := int(uint(i+j) >> 1) 120 if w.totals[h] < x { 121 i = h + 1 122 } else { 123 j = h 124 } 125 } 126 return i 127 } 128 129 var idWorkers, _ = NewIDWorker(0) 130 131 func UUID() int64 { 132 id, _ := idWorkers.ID() 133 return id 134 }