improve:点名
点名算法优化
This commit is contained in:
@@ -32,6 +32,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
public List<string> History { get; set; } = new List<string>();
|
||||
public Dictionary<string, int> NameFrequency { get; set; } = new Dictionary<string, int>();
|
||||
public Dictionary<string, double> NameProbabilities { get; set; } = new Dictionary<string, double>();
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用机器学习算法选择单个人员
|
||||
/// 使用概率算法选择单个人员
|
||||
/// </summary>
|
||||
private static string SelectSingleNameWithML(List<string> availableNames, List<string> alreadySelected, Random random)
|
||||
{
|
||||
if (availableNames.Count == 0) return null;
|
||||
if (availableNames.Count == 1) return availableNames[0];
|
||||
|
||||
// 计算每个人员的权重
|
||||
var nameWeights = new Dictionary<string, double>();
|
||||
// 确保历史数据已初始化
|
||||
if (historyData == null)
|
||||
{
|
||||
LoadRollCallHistory();
|
||||
}
|
||||
|
||||
// 初始化概率字典
|
||||
if (historyData.NameProbabilities == null)
|
||||
{
|
||||
historyData.NameProbabilities = new Dictionary<string, double>();
|
||||
}
|
||||
|
||||
// 获取每个人员的概率
|
||||
var nameProbabilities = new Dictionary<string, double>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取人员的概率
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据最近历史记录调整概率
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于概率的随机选择
|
||||
/// </summary>
|
||||
private static string ProbabilityBasedRandomSelection(Dictionary<string, double> 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -600,7 +724,7 @@ namespace Ink_Canvas
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加权随机选择
|
||||
/// 加权随机选择(保留用于兼容,实际已改用概率选择)
|
||||
/// </summary>
|
||||
private static string WeightedRandomSelection(Dictionary<string, double> nameWeights, Random random)
|
||||
{
|
||||
@@ -639,29 +763,72 @@ namespace Ink_Canvas
|
||||
|
||||
lock (historyLock)
|
||||
{
|
||||
// 初始化概率字典
|
||||
if (historyData.NameProbabilities == null)
|
||||
{
|
||||
historyData.NameProbabilities = new Dictionary<string, double>();
|
||||
}
|
||||
|
||||
// 更新历史记录
|
||||
if (historyData.History == null)
|
||||
historyData.History = new List<string>();
|
||||
|
||||
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<string, int>();
|
||||
// 更新频率统计
|
||||
if (historyData.NameFrequency == null)
|
||||
historyData.NameFrequency = new Dictionary<string, int>();
|
||||
|
||||
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<string>(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<string, double>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user