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  }