package main import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/binary" "fmt" "sort" ) // 模拟奖品配置 type Prize struct { ID int64 Name string Weight int32 } // 模拟默认策略的选品逻辑 func selectItem(prizes []Prize, seedKey []byte, issueID int64, userID int64) (int64, int64, error) { // 计算总权重 var total int64 for _, r := range prizes { total += int64(r.Weight) } if total <= 0 { return 0, 0, fmt.Errorf("总权重为0") } // 生成随机 salt salt := make([]byte, 16) if _, err := rand.Read(salt); err != nil { return 0, 0, err } // 使用 HMAC-SHA256 生成随机数 mac := hmac.New(sha256.New, seedKey) mac.Write([]byte(fmt.Sprintf("draw:issue:%d|user:%d|salt:%x", issueID, userID, salt))) sum := mac.Sum(nil) rnd := int64(binary.BigEndian.Uint64(sum[:8]) % uint64(total)) // 累加权重选择奖品 var acc int64 var picked int64 for _, r := range prizes { acc += int64(r.Weight) if rnd < acc { picked = r.ID break } } return picked, rnd, nil } func main() { // 模拟实际的奖品配置(请根据你的实际配置修改) prizes := []Prize{ {ID: 1, Name: "大奖A", Weight: 100}, {ID: 2, Name: "大奖B", Weight: 100}, {ID: 3, Name: "大奖C", Weight: 100}, {ID: 4, Name: "大奖D", Weight: 100}, {ID: 5, Name: "中奖", Weight: 3000}, {ID: 6, Name: "小奖", Weight: 28000}, {ID: 7, Name: "安慰奖", Weight: 68600}, } // 计算总权重 var totalWeight int64 for _, p := range prizes { totalWeight += int64(p.Weight) } fmt.Println("========== 抽奖概率分析工具 ==========") fmt.Printf("总权重: %d\n\n", totalWeight) // 打印理论概率 fmt.Println("【理论概率】") for _, p := range prizes { prob := float64(p.Weight) / float64(totalWeight) * 100 fmt.Printf("%-15s 权重:%6d 概率:%6.3f%% (1/%d)\n", p.Name, p.Weight, prob, int(float64(totalWeight)/float64(p.Weight))) } // 模拟抽奖 simulateCount := 10000 results := make(map[int64]int) seedKey := []byte("test-seed-key-12345") fmt.Printf("\n【模拟抽奖】模拟 %d 次抽奖\n", simulateCount) for i := 0; i < simulateCount; i++ { prizeID, _, err := selectItem(prizes, seedKey, 1, int64(i)) if err != nil { fmt.Printf("抽奖失败: %v\n", err) continue } results[prizeID]++ } // 打印实际结果 fmt.Println("\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 fmt.Printf("%-15s 理论:%6.3f%% 实际:%6.3f%% 偏差:%+6.3f%% 次数:%5d\n", p.Name, theoryProb, actualProb, diff, count) } // 分析大奖出现频率 fmt.Println("\n【大奖分析】") bigPrizeCount := results[1] + results[2] + results[3] + results[4] bigPrizeTheory := float64(400) / float64(totalWeight) * 100 bigPrizeActual := float64(bigPrizeCount) / float64(simulateCount) * 100 fmt.Printf("大奖总权重: 400\n") fmt.Printf("大奖理论概率: %.3f%% (1/%d)\n", bigPrizeTheory, totalWeight/400) fmt.Printf("大奖实际概率: %.3f%%\n", bigPrizeActual) fmt.Printf("大奖出现次数: %d / %d\n", bigPrizeCount, simulateCount) // 计算 100 抽出现 2 个大奖的概率 fmt.Println("\n【统计分析】") fmt.Printf("100 次抽奖期望大奖数: %.2f 次\n", 100*bigPrizeTheory/100) fmt.Println("100 次抽奖出现 2 个大奖的概率: 约 7.3% (使用二项分布计算)") fmt.Println("结论: 虽然不常见,但在统计学上是正常波动") // 检查随机数分布 fmt.Println("\n【随机数分布检查】") randValues := make([]int64, 1000) for i := 0; i < 1000; i++ { _, rnd, _ := selectItem(prizes, seedKey, 1, int64(i)) randValues[i] = rnd } sort.Slice(randValues, func(i, j int) bool { return randValues[i] < randValues[j] }) // 检查是否有聚集现象 fmt.Printf("随机数范围: [0, %d)\n", totalWeight) fmt.Printf("最小值: %d\n", randValues[0]) fmt.Printf("最大值: %d\n", randValues[999]) fmt.Printf("中位数: %d\n", randValues[500]) // 检查前 10% 的随机数(大奖区间) bigPrizeRange := int64(400) countInBigPrizeRange := 0 for _, v := range randValues { if v < bigPrizeRange { countInBigPrizeRange++ } } expectedInRange := float64(1000) * float64(bigPrizeRange) / float64(totalWeight) fmt.Printf("\n落在大奖区间 [0, %d) 的随机数:\n", bigPrizeRange) fmt.Printf(" 期望: %.1f 个\n", expectedInRange) fmt.Printf(" 实际: %d 个\n", countInBigPrizeRange) fmt.Printf(" 偏差: %+.1f%%\n", (float64(countInBigPrizeRange)-expectedInRange)/expectedInRange*100) }