From f7abc4e941c3b16e0b67f8175af6b30e0591e033 Mon Sep 17 00:00:00 2001 From: tx7do Date: Mon, 23 Sep 2024 21:31:56 +0800 Subject: [PATCH] feat: math. --- math/math.go | 27 ++++ rand/rand.go | 131 ++++++++++++++++- rand/rand_test.go | 285 ++++++++++++++++++++++++++++++++++++- uuid/test_trans.go | 7 - uuid/{trans.go => uuid.go} | 1 + uuid/uuid_test.go | 66 +++++++++ 6 files changed, 502 insertions(+), 15 deletions(-) delete mode 100644 uuid/test_trans.go rename uuid/{trans.go => uuid.go} (99%) create mode 100644 uuid/uuid_test.go diff --git a/math/math.go b/math/math.go index c03b55a..6fa05a0 100644 --- a/math/math.go +++ b/math/math.go @@ -42,3 +42,30 @@ func StandardDeviation(num []float64) float64 { var variance = Variance(mean, num) return math.Sqrt(variance) } + +// SumInt 计算整数数组的和 +func SumInt[T int | int32 | int64](array []T) int64 { + var sum int64 + for _, v := range array { + sum = sum + int64(v) + } + return sum +} + +// SumUint 计算整数数组的和 +func SumUint[T uint | uint32 | uint64](array []T) uint64 { + var sum uint64 + for _, v := range array { + sum = sum + uint64(v) + } + return sum +} + +// SumFloat 计算浮点数数组的和 +func SumFloat[T float32 | float64](array []T) float64 { + var sum float64 + for _, v := range array { + sum = sum + float64(v) + } + return sum +} diff --git a/rand/rand.go b/rand/rand.go index 865a56f..664815b 100644 --- a/rand/rand.go +++ b/rand/rand.go @@ -3,16 +3,50 @@ package rand import ( "math/rand" "time" + + "github.com/tx7do/go-utils/math" ) -var RANDOM = rand.New(rand.NewSource(time.Now().UnixNano())) +var rnd = rand.New(rand.NewSource(time.Now().UnixNano())) + +func init() { + rnd = rand.New(rand.NewSource(time.Now().UnixNano())) +} + +func Float32() float32 { + return rnd.Float32() +} + +func Float64() float64 { + return rnd.Float64() +} + +func Intn(n int) int { + return rnd.Intn(n) +} + +func Int31n(n int32) int32 { + return rnd.Int31n(n) +} + +func Int63n(n int64) int64 { + return rnd.Int63n(n) +} // RandomInt 根据区间产生随机数 func RandomInt(min, max int) int { if min >= max { return max } - return RANDOM.Intn(max-min) + min + return min + Intn(max-min+1) +} + +// RandomInt32 根据区间产生随机数 +func RandomInt32(min, max int32) int32 { + if min >= max { + return max + } + return min + Int31n(max-min+1) } // RandomInt64 根据区间产生随机数 @@ -20,5 +54,96 @@ func RandomInt64(min, max int64) int64 { if min >= max { return max } - return RANDOM.Int63n(max-min) + min + return min + Int63n(max-min+1) +} + +// RandomString 随机字符串,包含大小写字母和数字 +func RandomString(l int) string { + bytes := make([]byte, l) + for i := 0; i < l; i++ { + x := Intn(3) + switch x { + case 0: + bytes[i] = byte(RandomInt(65, 90)) //大写字母 + case 1: + bytes[i] = byte(RandomInt(97, 122)) + case 2: + bytes[i] = byte(Intn(10)) + } + } + return string(bytes) +} + +// RandomChoice 随机选择数组中的元素 +func RandomChoice[T any](array []T, n int) []T { + if n <= 0 { + return nil + } + if n == 1 { + return []T{array[Intn(len(array))]} + } + + tmp := make([]T, len(array)) + copy(tmp, array) + if len(tmp) <= n { + return tmp + } + + Shuffle(tmp) + + return tmp[:n] +} + +// Shuffle 随机打乱数组 +func Shuffle[T any](array []T) { + if array == nil { + return + } + + for i := range array { + j := Intn(i + 1) + array[i], array[j] = array[j], array[i] + } +} + +// WeightedChoice 根据权重随机,返回对应选项的索引,O(n) +func WeightedChoice(weightArray []int) int { + if weightArray == nil { + return -1 + } + + total := math.SumInt(weightArray) + rv := 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 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 := Int63n(total) + for i, v := range weightArray { + if rv < int64(v) { + return i + } + rv -= int64(v) + } + + return len(weightArray) - 1 } diff --git a/rand/rand_test.go b/rand/rand_test.go index ababa70..7cfe084 100644 --- a/rand/rand_test.go +++ b/rand/rand_test.go @@ -7,11 +7,286 @@ import ( "github.com/stretchr/testify/assert" ) -func TestRandomInt(t *testing.T) { +func TestFloat32_GeneratesValueWithinRange(t *testing.T) { for i := 0; i < 1000; i++ { - n := RandomInt(1, 100) - fmt.Println(n) - assert.True(t, n >= 1) - assert.True(t, n < 100) + value := Float32() + assert.True(t, value >= 0.0) + assert.True(t, value < 1.0) } } + +func TestFloat32_GeneratesDifferentValues(t *testing.T) { + values := make(map[float32]bool) + for i := 0; i < 1000; i++ { + value := Float32() + values[value] = true + } + assert.Greater(t, len(values), 1) +} + +func TestFloat64_GeneratesValueWithinRange(t *testing.T) { + for i := 0; i < 1000; i++ { + value := Float64() + assert.True(t, value >= 0.0) + assert.True(t, value < 1.0) + } +} + +func TestFloat64_GeneratesDifferentValues(t *testing.T) { + values := make(map[float64]bool) + for i := 0; i < 1000; i++ { + value := Float64() + values[value] = true + } + assert.Greater(t, len(values), 1) +} + +func TestRandomInt(t *testing.T) { + for i := 0; i < 1000; i++ { + n := RandomInt(1, 10) + fmt.Println(n) + assert.True(t, n >= 1) + assert.True(t, n <= 100) + } +} + +func TestRandomInt_MinEqualsMax(t *testing.T) { + n := RandomInt(5, 5) + assert.Equal(t, 5, n) +} + +func TestRandomInt_MinGreaterThanMax(t *testing.T) { + n := RandomInt(10, 5) + assert.Equal(t, 5, n) +} + +func TestRandomInt_NegativeRange(t *testing.T) { + for i := 0; i < 1000; i++ { + n := RandomInt(-10, -1) + assert.True(t, n >= -10) + assert.True(t, n <= -1) + } +} + +func TestRandomInt_ZeroRange(t *testing.T) { + for i := 0; i < 1000; i++ { + n := RandomInt(0, 0) + assert.Equal(t, 0, n) + } +} + +func TestShuffle_EmptyArray(t *testing.T) { + var array []int + Shuffle(array) + assert.Equal(t, array, array) +} + +func TestShuffle_SingleElementArray(t *testing.T) { + array := []int{1} + Shuffle(array) + assert.Equal(t, []int{1}, array) +} + +func TestShuffle_MultipleElementsArray(t *testing.T) { + array := []int{1, 2, 3, 4, 5} + original := make([]int, len(array)) + copy(original, array) + Shuffle(array) + assert.ElementsMatch(t, original, array) +} + +func TestShuffle_ArrayWithDuplicates(t *testing.T) { + array := []int{1, 2, 2, 3, 3, 3} + original := make([]int, len(array)) + copy(original, array) + Shuffle(array) + fmt.Println(array) + assert.ElementsMatch(t, original, array) +} + +func TestRandomChoice_EmptyArray(t *testing.T) { + result := RandomChoice([]int{}, 3) + assert.Nil(t, result) +} + +func TestRandomChoice_NegativeN(t *testing.T) { + array := []int{1, 2, 3} + result := RandomChoice(array, -1) + assert.Nil(t, result) +} + +func TestRandomChoice_ZeroN(t *testing.T) { + array := []int{1, 2, 3} + result := RandomChoice(array, 0) + assert.Nil(t, result) +} + +func TestRandomChoice_NGreaterThanArrayLength(t *testing.T) { + array := []int{1, 2, 3} + result := RandomChoice(array, 5) + assert.ElementsMatch(t, array, result) +} + +func TestRandomChoice_NEqualToArrayLength(t *testing.T) { + array := []int{1, 2, 3} + result := RandomChoice(array, 3) + assert.ElementsMatch(t, array, result) +} + +func TestRandomChoice_NLessThanArrayLength(t *testing.T) { + array := []int{1, 2, 3, 4, 5} + result := RandomChoice(array, 3) + assert.Len(t, result, 3) +} + +func TestRandomInt32_MinEqualsMax(t *testing.T) { + result := RandomInt32(5, 5) + assert.Equal(t, int32(5), result) +} + +func TestRandomInt32_MinGreaterThanMax(t *testing.T) { + result := RandomInt32(10, 5) + assert.Equal(t, int32(5), result) +} + +func TestRandomInt32_PositiveRange(t *testing.T) { + for i := 0; i < 1000; i++ { + result := RandomInt32(1, 10) + assert.True(t, result >= 1) + assert.True(t, result <= 10) + } +} + +func TestRandomInt32_NegativeRange(t *testing.T) { + for i := 0; i < 1000; i++ { + result := RandomInt32(-10, -1) + assert.True(t, result >= -10) + assert.True(t, result <= -1) + } +} + +func TestRandomInt32_ZeroRange(t *testing.T) { + for i := 0; i < 1000; i++ { + result := RandomInt32(0, 0) + assert.Equal(t, int32(0), result) + } +} + +func TestRandomInt64_MinEqualsMax(t *testing.T) { + result := RandomInt64(5, 5) + assert.Equal(t, int64(5), result) +} + +func TestRandomInt64_MinGreaterThanMax(t *testing.T) { + result := RandomInt64(10, 5) + assert.Equal(t, int64(5), result) +} + +func TestRandomInt64_PositiveRange(t *testing.T) { + for i := 0; i < 1000; i++ { + result := RandomInt64(1, 10) + assert.True(t, result >= 1) + assert.True(t, result <= 10) + } +} + +func TestRandomInt64_NegativeRange(t *testing.T) { + for i := 0; i < 1000; i++ { + result := RandomInt64(-10, -1) + assert.True(t, result >= -10) + assert.True(t, result <= -1) + } +} + +func TestRandomInt64_ZeroRange(t *testing.T) { + for i := 0; i < 1000; i++ { + result := RandomInt64(0, 0) + assert.Equal(t, int64(0), result) + } +} + +func TestRandomString_LengthZero(t *testing.T) { + result := RandomString(0) + assert.Equal(t, "", result) +} + +func TestRandomString_PositiveLength(t *testing.T) { + result := RandomString(10) + assert.Len(t, result, 10) +} + +func TestRandomString_ContainsOnlyValidCharacters(t *testing.T) { + result := RandomString(100) + for _, char := range result { + assert.True(t, (char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9')) + } +} + +func TestRandomString_NegativeLength(t *testing.T) { + result := RandomString(-5) + assert.Equal(t, "", result) +} + +func TestWeightedChoice_EmptyArray(t *testing.T) { + result := WeightedChoice([]int{}) + assert.Equal(t, -1, result) +} + +func TestWeightedChoice_AllZeroWeights(t *testing.T) { + result := WeightedChoice([]int{0, 0, 0}) + assert.Equal(t, 2, result) +} + +func TestWeightedChoice_NegativeWeights(t *testing.T) { + result := WeightedChoice([]int{-1, -2, -3}) + assert.Equal(t, 2, result) +} + +func TestWeightedChoice_MixedWeights(t *testing.T) { + weightArray := []int{1, 0, 3, 0, 2} + counts := make([]int, len(weightArray)) + for i := 0; i < 1000; i++ { + choice := WeightedChoice(weightArray) + counts[choice]++ + } + assert.Greater(t, counts[0], 0) + assert.Greater(t, counts[2], 0) + assert.Greater(t, counts[4], 0) +} + +func TestWeightedChoice_SingleElement(t *testing.T) { + result := WeightedChoice([]int{5}) + assert.Equal(t, 0, result) +} + +func TestNonWeightedChoice_EmptyArray(t *testing.T) { + result := NonWeightedChoice([]int{}) + assert.Equal(t, -1, result) +} + +func TestNonWeightedChoice_AllZeroWeights(t *testing.T) { + result := NonWeightedChoice([]int{0, 0, 0}) + assert.Equal(t, 2, result) +} + +func TestNonWeightedChoice_NegativeWeights(t *testing.T) { + result := NonWeightedChoice([]int{-1, -2, -3}) + assert.Equal(t, 2, result) +} + +func TestNonWeightedChoice_MixedWeights(t *testing.T) { + weightArray := []int{1, 0, 3, 0, 2} + counts := make([]int, len(weightArray)) + for i := 0; i < 1000; i++ { + choice := NonWeightedChoice(weightArray) + counts[choice]++ + } + assert.Greater(t, counts[0], 0) + assert.Greater(t, counts[2], 0) + assert.Greater(t, counts[4], 0) +} + +func TestNonWeightedChoice_SingleElement(t *testing.T) { + result := NonWeightedChoice([]int{5}) + assert.Equal(t, 0, result) +} diff --git a/uuid/test_trans.go b/uuid/test_trans.go deleted file mode 100644 index 3e2ae66..0000000 --- a/uuid/test_trans.go +++ /dev/null @@ -1,7 +0,0 @@ -package uuid - -import "testing" - -func TestUUID(t *testing.T) { - -} diff --git a/uuid/trans.go b/uuid/uuid.go similarity index 99% rename from uuid/trans.go rename to uuid/uuid.go index 3cd0cf6..1efd4b8 100644 --- a/uuid/trans.go +++ b/uuid/uuid.go @@ -2,6 +2,7 @@ package uuid import ( "github.com/google/uuid" + "github.com/tx7do/go-utils/trans" ) diff --git a/uuid/uuid_test.go b/uuid/uuid_test.go new file mode 100644 index 0000000..e01edef --- /dev/null +++ b/uuid/uuid_test.go @@ -0,0 +1,66 @@ +package uuid + +import ( + "testing" + + "github.com/google/uuid" +) + +func TestUUID(t *testing.T) { + t.Run("ToUuidPtr_NilString", func(t *testing.T) { + var str *string + result := ToUuidPtr(str) + if result != nil { + t.Errorf("expected nil, got %v", result) + } + }) + + t.Run("ToUuidPtr_ValidString", func(t *testing.T) { + str := "550e8400-e29b-41d4-a716-446655440000" + result := ToUuidPtr(&str) + if result == nil || result.String() != str { + t.Errorf("expected %v, got %v", str, result) + } + }) + + t.Run("ToUuidPtr_InvalidString", func(t *testing.T) { + str := "invalid-uuid" + result := ToUuidPtr(&str) + if result != nil { + t.Errorf("expected nil, got %v", result) + } + }) + + t.Run("ToUuid_ValidString", func(t *testing.T) { + str := "550e8400-e29b-41d4-a716-446655440000" + result := ToUuid(str) + if result.String() != str { + t.Errorf("expected %v, got %v", str, result) + } + }) + + t.Run("ToUuid_InvalidString", func(t *testing.T) { + str := "invalid-uuid" + result := ToUuid(str) + if result.String() == str { + t.Errorf("expected invalid UUID, got %v", result) + } + }) + + t.Run("ToStringPtr_NilUUID", func(t *testing.T) { + var id *uuid.UUID + result := ToStringPtr(id) + if result != nil { + t.Errorf("expected nil, got %v", result) + } + }) + + t.Run("ToStringPtr_ValidUUID", func(t *testing.T) { + id := uuid.MustParse("550e8400-e29b-41d4-a716-446655440000") + result := ToStringPtr(&id) + expected := "550e8400-e29b-41d4-a716-446655440000" + if result == nil || *result != expected { + t.Errorf("expected %v, got %v", expected, result) + } + }) +}