feat: Randomizer
This commit is contained in:
10
rand/rand.go
10
rand/rand.go
@@ -1,16 +1,16 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/tx7do/go-utils/math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
var rnd = rand.New(rand.NewSource(Seed(UnixNanoSeed)))
|
||||
|
||||
func init() {
|
||||
rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
if rnd != nil {
|
||||
rnd = rand.New(rand.NewSource(Seed(UnixNanoSeed)))
|
||||
}
|
||||
}
|
||||
|
||||
func Float32() float32 {
|
||||
|
||||
179
rand/randomizer.go
Normal file
179
rand/randomizer.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
"github.com/tx7do/go-utils/math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
type Randomizer struct {
|
||||
rnd *rand.Rand
|
||||
}
|
||||
|
||||
func NewRandomizer(seedType SeedType) *Randomizer {
|
||||
return &Randomizer{
|
||||
rnd: rand.New(rand.NewSource(Seed(seedType))),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Randomizer) Float32() float32 {
|
||||
return r.rnd.Float32()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Float64() float64 {
|
||||
return r.rnd.Float64()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int() int {
|
||||
return r.rnd.Int()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int31() int32 {
|
||||
return r.rnd.Int31()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int63() int64 {
|
||||
return r.rnd.Int63()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Uint32() uint32 {
|
||||
return r.rnd.Uint32()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Uint64() uint64 {
|
||||
return r.rnd.Uint64()
|
||||
}
|
||||
|
||||
func (r *Randomizer) Intn(n int) int {
|
||||
return r.rnd.Intn(n)
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int31n(n int32) int32 {
|
||||
return r.rnd.Int31n(n)
|
||||
}
|
||||
|
||||
func (r *Randomizer) Int63n(n int64) int64 {
|
||||
return r.rnd.Int63n(n)
|
||||
}
|
||||
|
||||
// RangeInt 根据区间产生随机数
|
||||
func (r *Randomizer) RangeInt(min, max int) int {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Intn(max-min+1)
|
||||
}
|
||||
|
||||
// RangeInt32 根据区间产生随机数
|
||||
func (r *Randomizer) RangeInt32(min, max int32) int32 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Int31n(max-min+1)
|
||||
}
|
||||
|
||||
// RangeInt64 根据区间产生随机数
|
||||
func (r *Randomizer) RangeInt64(min, max int64) int64 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Int63n(max-min+1)
|
||||
}
|
||||
|
||||
// RangeUint 根据区间产生随机数
|
||||
func (r *Randomizer) RangeUint(min, max uint) uint {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + uint(r.Intn(int(max-min+1)))
|
||||
}
|
||||
|
||||
// RangeUint32 根据区间产生随机数
|
||||
func (r *Randomizer) RangeUint32(min, max uint32) uint32 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + uint32(r.Int31n(int32(max-min+1)))
|
||||
}
|
||||
|
||||
// RangeUint64 根据区间产生随机数
|
||||
func (r *Randomizer) RangeUint64(min, max uint64) uint64 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + uint64(r.Int63n(int64(max-min+1)))
|
||||
}
|
||||
|
||||
// RangeFloat32 根据区间产生随机数
|
||||
func (r *Randomizer) RangeFloat32(min, max float32) float32 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Float32()*(max-min)
|
||||
}
|
||||
|
||||
// RangeFloat64 根据区间产生随机数
|
||||
func (r *Randomizer) RangeFloat64(min, max float64) float64 {
|
||||
if min >= max {
|
||||
return max
|
||||
}
|
||||
return min + r.Float64()*(max-min)
|
||||
}
|
||||
|
||||
// RandomString 随机字符串,包含大小写字母和数字
|
||||
func (r *Randomizer) RandomString(l int) string {
|
||||
bytes := make([]byte, l)
|
||||
for i := 0; i < l; i++ {
|
||||
x := r.Intn(3)
|
||||
switch x {
|
||||
case 0:
|
||||
bytes[i] = byte(r.RangeInt(65, 90)) //大写字母
|
||||
case 1:
|
||||
bytes[i] = byte(r.RangeInt(97, 122))
|
||||
case 2:
|
||||
bytes[i] = byte(r.Intn(10))
|
||||
}
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
// WeightedChoice 根据权重随机,返回对应选项的索引,O(n)
|
||||
func (r *Randomizer) WeightedChoice(weightArray []int) int {
|
||||
if weightArray == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
total := math.SumInt(weightArray)
|
||||
rv := r.Int63n(total)
|
||||
for i, v := range weightArray {
|
||||
if rv < int64(v) {
|
||||
return i
|
||||
}
|
||||
rv -= int64(v)
|
||||
}
|
||||
|
||||
return len(weightArray) - 1
|
||||
}
|
||||
|
||||
// NonWeightedChoice 根据权重随机,返回对应选项的索引,O(n). 权重大于等于0
|
||||
func (r *Randomizer) NonWeightedChoice(weightArray []int) int {
|
||||
if weightArray == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
for i, weight := range weightArray {
|
||||
if weight < 0 {
|
||||
weightArray[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
total := math.SumInt(weightArray)
|
||||
rv := r.Int63n(total)
|
||||
for i, v := range weightArray {
|
||||
if rv < int64(v) {
|
||||
return i
|
||||
}
|
||||
rv -= int64(v)
|
||||
}
|
||||
|
||||
return len(weightArray) - 1
|
||||
}
|
||||
86
rand/randomizer_test.go
Normal file
86
rand/randomizer_test.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRandomizer_RangeUint32_MinEqualsMax(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RangeUint32(5, 5)
|
||||
assert.Equal(t, uint32(5), result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint32_MinGreaterThanMax(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RangeUint32(10, 5)
|
||||
assert.Equal(t, uint32(5), result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint32_PositiveRange(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
for i := 0; i < 1000; i++ {
|
||||
result := r.RangeUint32(1, 10)
|
||||
assert.True(t, result >= 1)
|
||||
assert.True(t, result <= 10)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint64_MinEqualsMax(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RangeUint64(5, 5)
|
||||
assert.Equal(t, uint64(5), result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint64_MinGreaterThanMax(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RangeUint64(10, 5)
|
||||
assert.Equal(t, uint64(5), result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RangeUint64_PositiveRange(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
for i := 0; i < 1000; i++ {
|
||||
result := r.RangeUint64(1, 10)
|
||||
assert.True(t, result >= 1)
|
||||
assert.True(t, result <= 10)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomizer_WeightedChoice_EmptyArray(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.WeightedChoice([]int{})
|
||||
assert.Equal(t, -1, result)
|
||||
}
|
||||
|
||||
func TestRandomizer_WeightedChoice_AllZeroWeights(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.WeightedChoice([]int{0, 0, 0})
|
||||
assert.Equal(t, 2, result)
|
||||
}
|
||||
|
||||
func TestRandomizer_WeightedChoice_MixedWeights(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
weightArray := []int{1, 0, 3, 0, 2}
|
||||
counts := make([]int, len(weightArray))
|
||||
for i := 0; i < 1000; i++ {
|
||||
choice := r.WeightedChoice(weightArray)
|
||||
counts[choice]++
|
||||
}
|
||||
assert.Greater(t, counts[0], 0)
|
||||
assert.Greater(t, counts[2], 0)
|
||||
assert.Greater(t, counts[4], 0)
|
||||
}
|
||||
|
||||
func TestRandomizer_RandomString_LengthZero(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
result := r.RandomString(0)
|
||||
assert.Equal(t, "", result)
|
||||
}
|
||||
|
||||
func TestRandomizer_RandomString_CorrectLength(t *testing.T) {
|
||||
r := NewRandomizer(UnixNanoSeed)
|
||||
length := 50
|
||||
result := r.RandomString(length)
|
||||
assert.Equal(t, length, len(result))
|
||||
}
|
||||
Reference in New Issue
Block a user