package livestream import ( "encoding/json" "testing" "bindbox-game/internal/repository/mysql/model" ) // TestDiscreteRandomDistribution 测试随机离散分布的均匀性 func TestDiscreteRandomDistribution(t *testing.T) { // 模拟实际奖品配置 jsonData := `[{"id":58,"name":"Nu高达","weight":100},{"id":59,"name":"NT高达","weight":100},{"id":60,"name":"魔礼青","weight":100},{"id":61,"name":"维达尔","weight":100},{"id":62,"name":"玉衡星6号","weight":400},{"id":63,"name":"马克兔","weight":600},{"id":64,"name":"00高达","weight":700},{"id":65,"name":"SD随机款","weight":1100},{"id":66,"name":"兰博基尼","weight":3000},{"id":67,"name":"高达徽章","weight":28000},{"id":68,"name":"冰箱贴","weight":65800}]` var prizes []*model.LivestreamPrizes if err := json.Unmarshal([]byte(jsonData), &prizes); err != nil { t.Fatalf("解析奖品数据失败: %v", err) } // 计算总权重 var totalWeight int32 for _, p := range prizes { totalWeight += p.Weight } t.Logf("总权重: %d", totalWeight) // 生成随机离散位置 state, err := GenerateDiscretePositions(1, prizes) if err != nil { t.Fatalf("生成随机离散位置失败: %v", err) } // 模拟10000次抽奖 simulateCount := 10000 results := make(map[int64]int) for i := 0; i < simulateCount; i++ { // 模拟随机值 randValue := int32(i % int(totalWeight)) // 简化为顺序遍历,实际用crypto/rand // 查找对应的奖品 for prizeID, positions := range state.PrizePosMap { for _, pos := range positions { if pos == randValue { results[prizeID]++ break } } } } // 创建奖品ID到名称的映射 prizeMap := make(map[int64]string) for _, p := range prizes { prizeMap[p.ID] = p.Name } // 输出统计结果 t.Log("\n========== 随机离散分布测试结果 ==========") for _, p := range prizes { count := results[p.ID] theoryProb := float64(p.Weight) / float64(totalWeight) * 100 actualProb := float64(count) / float64(simulateCount) * 100 diff := actualProb - theoryProb t.Logf("%-20s 权重:%6d 理论:%6.2f%% 实际:%6.2f%% 偏差:%+6.2f%% 次数:%5d", p.Name, p.Weight, theoryProb, actualProb, diff, count) } } // TestContinuousVsDiscrete 对比连续区间和随机离散 func TestContinuousVsDiscrete(t *testing.T) { // 简化奖品配置 prizes := []*model.LivestreamPrizes{ {ID: 1, Name: "大奖", Weight: 100}, {ID: 2, Name: "小奖A", Weight: 3000}, {ID: 3, Name: "小奖B", Weight: 3000}, {ID: 4, Name: "冰箱贴", Weight: 65800}, } totalWeight := int32(100 + 3000 + 3000 + 65800) t.Logf("\n========== 连续区间 vs 随机离散对比 ==========") t.Logf("总权重: %d", totalWeight) t.Log("") // 连续区间模拟 t.Log("【连续区间方法】") t.Log("大奖区间: [0, 99] (0.1%)") t.Log("小奖A区间: [100, 3099] (3%)") t.Log("小奖B区间: [3100, 6099] (3%)") t.Log("冰箱贴区间: [6100, 71899] (65.8%)") t.Log("") // 随机离散模拟 state, err := GenerateDiscretePositions(1, prizes) if err != nil { t.Fatalf("生成随机离散位置失败: %v", err) } t.Log("【随机离散方法】") t.Logf("大奖的随机位置数: %d", len(state.PrizePosMap[1])) t.Logf("小奖A的随机位置数: %d", len(state.PrizePosMap[2])) t.Logf("小奖B的随机位置数: %d", len(state.PrizePosMap[3])) t.Logf("冰箱贴的随机位置数: %d", len(state.PrizePosMap[4])) // 验证位置分布 t.Log("\n位置分布示例(前10个):") if positions, ok := state.PrizePosMap[1]; ok && len(positions) > 0 { sampleSize := 10 if len(positions) < sampleSize { sampleSize = len(positions) } t.Logf("大奖位置: %v", positions[:sampleSize]) } if positions, ok := state.PrizePosMap[4]; ok && len(positions) > 0 { sampleSize := 10 if len(positions) < sampleSize { sampleSize = len(positions) } t.Logf("冰箱贴位置: %v", positions[:sampleSize]) } } // TestEdgeCases 测试边界情况 func TestEdgeCases(t *testing.T) { t.Run("空奖品列表", func(t *testing.T) { _, err := GenerateDiscretePositions(1, []*model.LivestreamPrizes{}) if err == nil { t.Error("应该返回错误") } }) t.Run("零权重", func(t *testing.T) { prizes := []*model.LivestreamPrizes{ {ID: 1, Name: "奖品", Weight: 0}, } _, err := GenerateDiscretePositions(1, prizes) if err == nil { t.Error("应该返回错误") } }) t.Run("单个奖品", func(t *testing.T) { prizes := []*model.LivestreamPrizes{ {ID: 1, Name: "唯一奖品", Weight: 1000}, } state, err := GenerateDiscretePositions(1, prizes) if err != nil { t.Fatalf("不应该出错: %v", err) } if len(state.PrizePosMap[1]) != 1000 { t.Errorf("位置数量错误,期望1000,实际%d", len(state.PrizePosMap[1])) } }) } // TestPositionUniqueness 测试位置的唯一性 func TestPositionUniqueness(t *testing.T) { prizes := []*model.LivestreamPrizes{ {ID: 1, Name: "大奖", Weight: 100}, {ID: 2, Name: "小奖", Weight: 3000}, {ID: 3, Name: "冰箱贴", Weight: 65800}, } state, err := GenerateDiscretePositions(1, prizes) if err != nil { t.Fatalf("生成失败: %v", err) } // 收集所有位置 allPositions := make(map[int32]int64) for prizeID, positions := range state.PrizePosMap { for _, pos := range positions { if existingPrizeID, exists := allPositions[pos]; exists { t.Errorf("位置 %d 被多个奖品占用: 奖品%d 和 奖品%d", pos, existingPrizeID, prizeID) } allPositions[pos] = prizeID } } expectedCount := 100 + 3000 + 65800 if len(allPositions) != expectedCount { t.Errorf("位置总数错误,期望%d,实际%d", expectedCount, len(allPositions)) } t.Logf("位置唯一性验证通过: %d 个唯一位置", len(allPositions)) } // BenchmarkDiscreteVsContinuous 性能对比测试 func BenchmarkDiscreteGeneration(b *testing.B) { prizes := []*model.LivestreamPrizes{ {ID: 1, Name: "大奖", Weight: 100}, {ID: 2, Name: "小奖A", Weight: 3000}, {ID: 3, Name: "小奖B", Weight: 3000}, {ID: 4, Name: "冰箱贴", Weight: 65800}, } b.ResetTimer() for i := 0; i < b.N; i++ { _, err := GenerateDiscretePositions(int64(i), prizes) if err != nil { b.Fatal(err) } } } // 打印结果辅助函数 func printResults(t *testing.T, results map[int64]int, prizes []*model.LivestreamPrizes, simulateCount int) { t.Log("\n========== 测试结果 ==========") for _, p := range prizes { count := results[p.ID] t.Logf("%-20s 中奖次数: %d (%.2f%%)", p.Name, count, float64(count)/float64(simulateCount)*100) } }