diff --git a/Ink Canvas/Windows/NewStyleRollCallWindow.cs b/Ink Canvas/Windows/NewStyleRollCallWindow.cs index 69658a54..6dca3418 100644 --- a/Ink Canvas/Windows/NewStyleRollCallWindow.cs +++ b/Ink Canvas/Windows/NewStyleRollCallWindow.cs @@ -32,6 +32,7 @@ namespace Ink_Canvas { public List History { get; set; } = new List(); public Dictionary NameFrequency { get; set; } = new Dictionary(); + public Dictionary NameProbabilities { get; set; } = new Dictionary(); public DateTime LastUpdate { get; set; } = DateTime.Now; } @@ -132,6 +133,13 @@ namespace Ink_Canvas private static double avoidanceWeight = 0.8; // 避免重复的权重 private const double FREQUENCY_WEIGHT = 0.2; // 频率平衡的权重 + // 概率相关 + private const double DEFAULT_PROBABILITY = 1.0; // 默认概率 + private const double BASE_PROBABILITY_DECAY_FACTOR = 0.5; // 基础概率衰减因子 + private const double MIN_PROBABILITY = 0.1; // 最小概率 + private const double PROBABILITY_RECOVERY_RATE = 0.1; // 概率恢复速率 + private const double FREQUENCY_BOOST_FACTOR = 0.5; // 频率平衡增强因子 + // 单次抽相关 private bool isSingleDrawMode = false; private Random singleDrawRandom = new Random(); @@ -531,38 +539,154 @@ namespace Ink_Canvas } /// - /// 使用机器学习算法选择单个人员 + /// 使用概率算法选择单个人员 /// private static string SelectSingleNameWithML(List availableNames, List alreadySelected, Random random) { if (availableNames.Count == 0) return null; if (availableNames.Count == 1) return availableNames[0]; - // 计算每个人员的权重 - var nameWeights = new Dictionary(); + // 确保历史数据已初始化 + if (historyData == null) + { + LoadRollCallHistory(); + } + + // 初始化概率字典 + if (historyData.NameProbabilities == null) + { + historyData.NameProbabilities = new Dictionary(); + } + + // 获取每个人员的概率 + var nameProbabilities = new Dictionary(); foreach (string name in availableNames) { if (alreadySelected.Contains(name)) continue; - double weight = 1.0; // 基础权重 - - // 1. 避免最近重复的权重计算 - double recentAvoidanceWeight = CalculateRecentAvoidanceWeight(name); - weight *= (1.0 - recentAvoidanceWeight * avoidanceWeight); - - // 2. 频率平衡权重计算 - double frequencyWeight = CalculateFrequencyWeight(name); - weight *= (1.0 + frequencyWeight * FREQUENCY_WEIGHT); - - // 3. 确保权重不为负数 - weight = Math.Max(weight, 0.1); - - nameWeights[name] = weight; + // 获取基础概率 + double baseProbability = GetNameProbability(name); + + // 根据最近历史记录调整概率 + double adjustedProbability = AdjustProbabilityByRecentHistory(name, baseProbability); + + double finalProbability = AdjustProbabilityByFrequency(name, adjustedProbability); + + nameProbabilities[name] = finalProbability; } - // 使用加权随机选择 - return WeightedRandomSelection(nameWeights, random); + // 使用概率进行加权随机选择 + return ProbabilityBasedRandomSelection(nameProbabilities, random); + } + + /// + /// 获取人员的概率 + /// + private static double GetNameProbability(string name) + { + if (historyData == null || historyData.NameProbabilities == null) + return DEFAULT_PROBABILITY; + + if (historyData.NameProbabilities.ContainsKey(name)) + { + return historyData.NameProbabilities[name]; + } + else + { + // 新人员,初始化默认概率 + historyData.NameProbabilities[name] = DEFAULT_PROBABILITY; + return DEFAULT_PROBABILITY; + } + } + + /// + /// 根据最近历史记录调整概率 + /// + private static double AdjustProbabilityByRecentHistory(string name, double baseProbability) + { + if (historyData == null || historyData.History == null || historyData.History.Count == 0) + return baseProbability; + + // 获取最近记录 + var recentHistory = historyData.History.Skip(Math.Max(0, historyData.History.Count - maxRecentHistory)).ToList(); + int recentCount = recentHistory.Count(n => n == name); + + if (recentCount == 0) + return baseProbability; + + double recentFrequency = (double)recentCount / Math.Min(recentHistory.Count, maxRecentHistory); + + double reductionFactor = 1.0 - (recentFrequency * avoidanceWeight); + reductionFactor = Math.Max(reductionFactor, MIN_PROBABILITY / DEFAULT_PROBABILITY); // 确保不会降得太低 + + return baseProbability * reductionFactor; + } + + private static double AdjustProbabilityByFrequency(string name, double baseProbability) + { + if (historyData == null || historyData.NameFrequency == null || historyData.NameFrequency.Count == 0) + return baseProbability; + + // 计算总选中次数 + int totalSelections = historyData.NameFrequency.Values.Sum(); + if (totalSelections == 0) + return baseProbability; + + // 获取该名字的选中次数 + int nameCount = historyData.NameFrequency.ContainsKey(name) ? historyData.NameFrequency[name] : 0; + + // 计算该名字的选中频率 + double nameFrequency = (double)nameCount / totalSelections; + + // 计算平均频率(假设有N个不同的人) + int uniqueNamesCount = historyData.NameFrequency.Keys.Count; + if (uniqueNamesCount == 0) + return baseProbability; + + double averageFrequency = 1.0 / uniqueNamesCount; + + // 如果该名字的频率低于平均频率,则增加概率 + if (nameFrequency < averageFrequency) + { + // 计算频率差异比例 + double frequencyRatio = nameFrequency / averageFrequency; + + double boostFactor = FREQUENCY_BOOST_FACTOR * (1.0 - frequencyRatio); + + // 增加概率 + double boostedProbability = baseProbability * (1.0 + boostFactor); + + // 限制最大概率,避免过高 + return Math.Min(boostedProbability, DEFAULT_PROBABILITY * 2.0); + } + + return baseProbability; + } + + /// + /// 基于概率的随机选择 + /// + private static string ProbabilityBasedRandomSelection(Dictionary nameProbabilities, Random random) + { + if (nameProbabilities.Count == 0) return null; + + double totalProbability = nameProbabilities.Values.Sum(); + if (totalProbability <= 0) return nameProbabilities.Keys.First(); + + double randomValue = random.NextDouble() * totalProbability; + double currentProbability = 0; + + foreach (var kvp in nameProbabilities) + { + currentProbability += kvp.Value; + if (randomValue <= currentProbability) + { + return kvp.Key; + } + } + + return nameProbabilities.Keys.Last(); } /// @@ -600,7 +724,7 @@ namespace Ink_Canvas } /// - /// 加权随机选择 + /// 加权随机选择(保留用于兼容,实际已改用概率选择) /// private static string WeightedRandomSelection(Dictionary nameWeights, Random random) { @@ -639,29 +763,72 @@ namespace Ink_Canvas lock (historyLock) { + // 初始化概率字典 + if (historyData.NameProbabilities == null) + { + historyData.NameProbabilities = new Dictionary(); + } + // 更新历史记录 if (historyData.History == null) historyData.History = new List(); historyData.History.AddRange(selectedNames); - // 保持历史记录不超过100条 - if (historyData.History.Count > 100) - { - historyData.History = historyData.History.Skip(historyData.History.Count - 100).ToList(); - } + // 保持历史记录不超过100条 + if (historyData.History.Count > 100) + { + historyData.History = historyData.History.Skip(historyData.History.Count - 100).ToList(); + } - // 更新频率统计 - if (historyData.NameFrequency == null) - historyData.NameFrequency = new Dictionary(); + // 更新频率统计 + if (historyData.NameFrequency == null) + historyData.NameFrequency = new Dictionary(); - foreach (string name in selectedNames) - { - if (historyData.NameFrequency.ContainsKey(name)) - historyData.NameFrequency[name]++; - else - historyData.NameFrequency[name] = 1; - } + // 更新概率:降重机制 + foreach (string name in selectedNames) + { + // 更新频率统计 + if (historyData.NameFrequency.ContainsKey(name)) + historyData.NameFrequency[name]++; + else + historyData.NameFrequency[name] = 1; + + // 降重:被选中的人员概率降低 + double currentProbability = GetNameProbability(name); + + double decayFactor = BASE_PROBABILITY_DECAY_FACTOR * (1.0 + avoidanceWeight); + decayFactor = Math.Min(decayFactor, 0.95); + + double newProbability = currentProbability * decayFactor; + newProbability = Math.Max(newProbability, MIN_PROBABILITY); // 确保不低于最小概率 + historyData.NameProbabilities[name] = newProbability; + } + + if (historyData.History != null && historyData.History.Count > 0) + { + int historyCount = historyData.History.Count; + int skipCount = Math.Max(0, historyCount - maxRecentHistory); + var recentHistory = historyData.History.Skip(skipCount).ToList(); + var recentNames = new HashSet(recentHistory); + + var allNames = historyData.NameProbabilities.Keys.ToList(); + foreach (string name in allNames) + { + if (!recentNames.Contains(name)) + { + double currentProbability = historyData.NameProbabilities[name]; + if (currentProbability < DEFAULT_PROBABILITY) + { + double newProbability = Math.Min( + currentProbability + PROBABILITY_RECOVERY_RATE, + DEFAULT_PROBABILITY + ); + historyData.NameProbabilities[name] = newProbability; + } + } + } + } historyData.LastUpdate = DateTime.Now; @@ -697,6 +864,11 @@ namespace Ink_Canvas if (data != null) { historyData = data; + // 确保概率字典已初始化 + if (historyData.NameProbabilities == null) + { + historyData.NameProbabilities = new Dictionary(); + } } else {