| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- // 分布概率验证测试
- // 测试 getWeightedRandomFromSeed 函数的分布概率
- // 复制核心函数实现
- function seedRandom(str) {
- let hash = 0
- for (let i = 0; i < str.length; i++) {
- const char = str.charCodeAt(i)
- hash = ((hash << 5) - hash) + char
- hash = hash & hash
- }
- return Math.abs(hash)
- }
- function getWeightedRandomFromSeed(array, weights, seed, offset = 0) {
- // 使用种子生成 0-1 之间的伪随机数
- const pseudoRandom = ((seed + offset) * 9301 + 49297) % 233280 / 233280.0
-
- // 累计权重
- let cumulativeWeight = 0
- const cumulativeWeights = weights.map(weight => cumulativeWeight += weight)
- const totalWeight = cumulativeWeights[cumulativeWeights.length - 1]
-
- // 找到对应的索引
- const randomValue = pseudoRandom * totalWeight
- const index = cumulativeWeights.findIndex(weight => randomValue <= weight)
-
- return array[index >= 0 ? index : 0]
- }
- // 测试配置
- const levels = [
- { name: '种', emoji: '🌱' },
- { name: '芽', emoji: '🍃' },
- { name: '叶', emoji: '🌿' },
- { name: '蕾', emoji: '🌸' },
- { name: '花', emoji: '🌺' },
- { name: '宝石权杖', emoji: '💎' }
- ]
- const levelProbabilities = [0.1, 0.2, 0.3, 0.3, 0.07, 0.03]
- // 生成大量测试样本
- function runDistributionTest(sampleSize = 100000) {
- console.log(`\n===== 分布概率验证测试 (样本数: ${sampleSize}) =====`)
- console.log('预期概率:', levelProbabilities.map((p, i) => `${levels[i].name}: ${(p * 100).toFixed(1)}%`).join(', '))
-
- const results = {}
- levels.forEach(level => results[level.name] = 0)
-
- // 生成测试样本
- for (let i = 0; i < sampleSize; i++) {
- // 使用不同的种子和偏移量来模拟真实使用场景
- const testSeed = seedRandom(`test_${i}`)
- const level = getWeightedRandomFromSeed(levels, levelProbabilities, testSeed, 6)
- results[level.name]++
- }
-
- // 计算实际概率
- console.log('\n实际分布:')
- levels.forEach((level, index) => {
- const actualCount = results[level.name]
- const actualProbability = actualCount / sampleSize
- const expectedProbability = levelProbabilities[index]
- const deviation = Math.abs(actualProbability - expectedProbability)
- const deviationPercent = (deviation / expectedProbability * 100).toFixed(2)
-
- console.log(`${level.emoji} ${level.name}: ${actualCount}次 (${(actualProbability * 100).toFixed(2)}%) | 偏差: ${deviationPercent}%`)
- })
-
- // 计算卡方检验
- let chiSquare = 0
- levels.forEach((level, index) => {
- const observed = results[level.name]
- const expected = sampleSize * levelProbabilities[index]
- chiSquare += Math.pow(observed - expected, 2) / expected
- })
-
- console.log(`\n卡方值: ${chiSquare.toFixed(4)}`)
- console.log(`自由度: ${levels.length - 1}`)
-
- // 简单的分布质量评估
- const maxExpectedDeviation = 0.02 // 2% 的最大预期偏差
- let isDistributionGood = true
-
- levels.forEach((level, index) => {
- const actualProbability = results[level.name] / sampleSize
- const expectedProbability = levelProbabilities[index]
- const deviation = Math.abs(actualProbability - expectedProbability)
-
- if (deviation > maxExpectedDeviation) {
- isDistributionGood = false
- }
- })
-
- console.log(`\n分布质量评估: ${isDistributionGood ? '✅ 良好' : '⚠️ 需要关注'}`)
-
- return {
- results,
- chiSquare,
- isDistributionGood
- }
- }
- // 测试种子确定性
- function testSeedDeterminism() {
- console.log('\n===== 种子确定性测试 =====')
-
- const testSeeds = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve']
-
- testSeeds.forEach(name => {
- const seed = seedRandom(name)
- const results = []
-
- // 同一个种子应该产生相同的结果
- for (let i = 0; i < 5; i++) {
- const level = getWeightedRandomFromSeed(levels, levelProbabilities, seed, 6)
- results.push(level.name)
- }
-
- const allSame = results.every(result => result === results[0])
- console.log(`${name} (种子: ${seed}): ${results[0]} ${allSame ? '✅' : '❌'}`)
- })
- }
- // 测试边界情况
- function testEdgeCases() {
- console.log('\n===== 边界情况测试 =====')
-
- // 测试极端权重
- const extremeWeights = [0, 0, 0, 0, 0, 1] // 只有最后一个有权重
- let result = getWeightedRandomFromSeed(levels, extremeWeights, 12345, 6)
- console.log(`极端权重测试: ${result.name} (应该是宝石权杖)`)
-
- // 测试相等权重
- const equalWeights = [1, 1, 1, 1, 1, 1]
- const equalResults = {}
- levels.forEach(level => equalResults[level.name] = 0)
-
- for (let i = 0; i < 10000; i++) {
- result = getWeightedRandomFromSeed(levels, equalWeights, i, 6)
- equalResults[result.name]++
- }
-
- console.log('相等权重分布:')
- levels.forEach(level => {
- const count = equalResults[level.name]
- const percentage = (count / 10000 * 100).toFixed(1)
- console.log(` ${level.name}: ${percentage}% (期望: ~16.7%)`)
- })
- }
- // 运行所有测试
- function runAllTests() {
- console.log('开始运行 getWeightedRandomFromSeed 分布概率验证测试...')
-
- // 小样本测试
- runDistributionTest(10000)
-
- // 大样本测试
- runDistributionTest(100000)
-
- // 种子确定性测试
- testSeedDeterminism()
-
- // 边界情况测试
- testEdgeCases()
-
- console.log('\n===== 测试完成 =====')
- }
- // 如果直接运行此文件
- if (typeof module !== 'undefined' && require.main === module) {
- runAllTests()
- }
- // 导出函数供其他测试使用
- if (typeof module !== 'undefined') {
- module.exports = {
- runDistributionTest,
- testSeedDeterminism,
- testEdgeCases,
- runAllTests
- }
- }
|