1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-05 05:12:26 +08:00
Files
lancet/random/random.go
2024-03-04 16:36:20 +08:00

336 lines
8.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license.
// Package random implements some basic functions to generate random int and string.
package random
import (
crand "crypto/rand"
"fmt"
"io"
"math"
"math/rand"
"time"
"unsafe"
"github.com/duke-git/lancet/mathutil"
)
const (
MaximumCapacity = math.MaxInt>>1 + 1
Numeral = "0123456789"
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
)
var rn = rand.NewSource(time.Now().UnixNano())
func init() {
rand.Seed(time.Now().UnixNano())
}
// RandInt generate random int between [min, max).
func RandInt(min, max int) int {
if min == max {
return min
}
if max < min {
min, max = max, min
}
return rand.Intn(max-min) + min
}
// RandFloat generate random float64 number between [min, max) with specific precision.
func RandFloat(min, max float64, precision int) float64 {
if min == max {
return min
}
if max < min {
min, max = max, min
}
n := rand.Float64()*(max-min) + min
return mathutil.RoundToFloat(n, precision)
}
// RandBytes generate random byte slice.
// Play: https://go.dev/play/p/EkiLESeXf8d
func RandBytes(length int) []byte {
if length < 1 {
return []byte{}
}
b := make([]byte, length)
if _, err := io.ReadFull(crand.Reader, b); err != nil {
return nil
}
return b
}
// RandString generate random alpha string of specified length.
func RandString(length int) string {
return random(Letters, length)
}
// RandUpper generate a random upper case string of specified length.
func RandUpper(length int) string {
return random(UpperLetters, length)
}
// RandLower generate a random lower case string of specified length.
func RandLower(length int) string {
return random(LowwerLetters, length)
}
// RandNumeral generate a random numeral string of specified length.
func RandNumeral(length int) string {
return random(Numeral, length)
}
// RandNumeralOrLetter generate a random numeral or alpha string of specified length.
func RandNumeralOrLetter(length int) string {
return random(Numeral+Letters, length)
}
// RandSymbolChar generate a random symbol char of specified length.
// symbol chars: !@#$%^&*()_+-=[]{}|;':\",./<>?.
func RandSymbolChar(length int) string {
return random(SymbolChars, length)
}
// nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂参考java8的hashmap的tableSizeFor函数
func nearestPowerOfTwo(cap int) int {
n := cap - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
if n < 0 {
return 1
} else if n >= MaximumCapacity {
return MaximumCapacity
}
return n + 1
}
// random generate a random string based on given string range.
func random(s string, length int) string {
// 仿照strings.Builder
// 创建一个长度为 length 的字节切片
bytes := make([]byte, length)
strLength := len(s)
if strLength <= 0 {
return ""
} else if strLength == 1 {
for i := 0; i < length; i++ {
bytes[i] = s[0]
}
return *(*string)(unsafe.Pointer(&bytes))
}
// s的字符需要使用多少个比特位数才能表示完
// letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快
letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(strLength))))
// 最大的字母id掩码
var letterIdMask int64 = 1<<letterIdBits - 1
// 可用次数的最大值
letterIdMax := 63 / letterIdBits
// 循环生成随机字符串
for i, cache, remain := length-1, rn.Int63(), letterIdMax; i >= 0; {
// 检查随机数生成器是否用尽所有随机数
if remain == 0 {
cache, remain = rn.Int63(), letterIdMax
}
// 从可用字符的字符串中随机选择一个字符
if idx := int(cache & letterIdMask); idx < strLength {
bytes[i] = s[idx]
i--
}
// 右移比特位数,为下次选择字符做准备
cache >>= letterIdBits
remain--
}
// 仿照strings.Builder用unsafe包返回一个字符串避免拷贝
// 将字节切片转换为字符串并返回
return *(*string)(unsafe.Pointer(&bytes))
}
// UUIdV4 generate a random UUID of version 4 according to RFC 4122.
func UUIdV4() (string, error) {
uuid := make([]byte, 16)
n, err := io.ReadFull(crand.Reader, uuid)
if n != len(uuid) || err != nil {
return "", err
}
uuid[8] = uuid[8]&^0xc0 | 0x80
uuid[6] = uuid[6]&^0xf0 | 0x40
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
}
// RandUniqueIntSlice generate a slice of random int of length n that do not repeat.
func RandUniqueIntSlice(n, min, max int) []int {
if min > max {
return []int{}
}
if n > max-min {
n = max - min
}
nums := make([]int, n)
used := make(map[int]struct{}, n)
for i := 0; i < n; {
r := RandInt(min, max)
if _, use := used[r]; use {
continue
}
used[r] = struct{}{}
nums[i] = r
i++
}
return nums
}
// RandFloats generate a slice of random float64 numbers of length n that do not repeat.
func RandFloats(n int, min, max float64, precision int) []float64 {
nums := make([]float64, n)
used := make(map[float64]struct{}, n)
for i := 0; i < n; {
r := RandFloat(min, max, precision)
if _, use := used[r]; use {
continue
}
used[r] = struct{}{}
nums[i] = r
i++
}
return nums
}
// const (
// NUMERAL = "0123456789"
// LOWER_LETTERS = "abcdefghijklmnopqrstuvwxyz"
// UPPER_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// )
// var rn = rand.NewSource(time.Now().UnixNano())
// func init() {
// rand.Seed(time.Now().UnixNano())
// }
// // RandInt generate random int between min and max, maybe min, not be max
// func RandInt(min, max int) int {
// if min == max {
// return min
// }
// if max < min {
// min, max = max, min
// }
// return rand.Intn(max-min) + min
// }
// // RandBytes generate random byte slice
// func RandBytes(length int) []byte {
// if length < 1 {
// return []byte{}
// }
// b := make([]byte, length)
// if _, err := io.ReadFull(crand.Reader, b); err != nil {
// return nil
// }
// return b
// }
// // RandString generate random string
// func RandString(length int) string {
// return random(LETTERS, length)
// }
// // RandUpper generate a random upper case string
// func RandUpper(length int) string {
// return random(UPPER_LETTERS, length)
// }
// // RandLower generate a random lower case string
// func RandLower(length int) string {
// return random(LOWER_LETTERS, length)
// }
// // RandNumeral generate a random numeral string
// func RandNumeral(length int) string {
// return random(NUMERAL, length)
// }
// // RandNumeralOrLetter generate a random numeral or letter string
// func RandNumeralOrLetter(length int) string {
// return random(NUMERAL+LETTERS, length)
// }
// // random generate a random string based on given string range
// func random(s string, length int) string {
// b := make([]byte, length)
// // fix: https://github.com/duke-git/lancet/issues/75
// // r := rand.New(rand.NewSource(time.Now().UnixNano()))
// for i := range b {
// b[i] = s[rand.Int63()%int64(len(s))]
// }
// return string(b)
// }
// // UUIdV4 generate a random UUID of version 4 according to RFC 4122
// func UUIdV4() (string, error) {
// uuid := make([]byte, 16)
// n, err := io.ReadFull(crand.Reader, uuid)
// if n != len(uuid) || err != nil {
// return "", err
// }
// uuid[8] = uuid[8]&^0xc0 | 0x80
// uuid[6] = uuid[6]&^0xf0 | 0x40
// return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
// }
// // RandUniqueIntSlice generate a slice of random int of length n that do not repeat.
// func RandUniqueIntSlice(n, min, max int) []int {
// if min > max {
// return []int{}
// }
// if n > max-min {
// n = max - min
// }
// nums := make([]int, n)
// used := make(map[int]struct{}, n)
// for i := 0; i < n; {
// r := RandInt(min, max)
// if _, use := used[r]; use {
// continue
// }
// used[r] = struct{}{}
// nums[i] = r
// i++
// }
// return nums
// }