improve:点名算法

改进不放回随机
This commit is contained in:
2025-12-20 19:57:11 +08:00
parent d011d2ba8a
commit 7f01e7acb6
2 changed files with 96 additions and 67 deletions
+52 -48
View File
@@ -537,20 +537,34 @@ namespace Ink_Canvas
bool enableML = MainWindow.Settings?.RandSettings?.EnableMLAvoidance ?? true;
if (!enableML)
{
// 如果禁用机器学习,使用简单随机选择
// 如果禁用机器学习,使用简单不放回随机选择
return SelectNamesRandomly(availableNames, count, random);
}
var candidatePool = new List<string>(availableNames);
var selectedNames = new List<string>();
var remainingNames = new List<string>(availableNames);
for (int i = 0; i < count && remainingNames.Count > 0; i++)
if (count >= candidatePool.Count)
{
string selectedName = SelectSingleNameWithML(remainingNames, selectedNames, random);
return new List<string>(candidatePool);
}
for (int i = 0; i < count && candidatePool.Count > 0; i++)
{
string selectedName = SelectSingleNameWithMLWithoutReplacement(candidatePool, selectedNames, random);
if (!string.IsNullOrEmpty(selectedName))
{
selectedNames.Add(selectedName);
remainingNames.Remove(selectedName);
candidatePool.Remove(selectedName);
}
else
{
if (candidatePool.Count > 0)
{
int randomIndex = random.Next(0, candidatePool.Count);
selectedName = candidatePool[randomIndex];
selectedNames.Add(selectedName);
candidatePool.RemoveAt(randomIndex);
}
}
}
@@ -558,21 +572,34 @@ namespace Ink_Canvas
}
/// <summary>
/// 简单随机选择点名人员
/// 简单不放回随机选择点名人员
/// </summary>
private static List<string> SelectNamesRandomly(List<string> availableNames, int count, Random random)
{
if (availableNames == null || availableNames.Count == 0)
return new List<string>();
var selectedNames = new List<string>();
var remainingNames = new List<string>(availableNames);
for (int i = 0; i < count && remainingNames.Count > 0; i++)
// 如果请求的数量大于或等于可用名单大小,返回所有名单
if (count >= availableNames.Count)
{
int randomIndex = random.Next(remainingNames.Count);
selectedNames.Add(remainingNames[randomIndex]);
remainingNames.RemoveAt(randomIndex);
return new List<string>(availableNames);
}
var candidatePool = new List<string>(availableNames);
var selectedNames = new List<string>();
for (int i = 0; i < count && candidatePool.Count > 0; i++)
{
int randomIndex = random.Next(0, candidatePool.Count);
selectedNames.Add(candidatePool[randomIndex]);
int lastIndex = candidatePool.Count - 1;
if (randomIndex != lastIndex)
{
candidatePool[randomIndex] = candidatePool[lastIndex];
}
candidatePool.RemoveAt(lastIndex);
}
return selectedNames;
@@ -581,10 +608,10 @@ namespace Ink_Canvas
/// <summary>
/// 使用概率算法选择单个人员
/// </summary>
private static string SelectSingleNameWithML(List<string> availableNames, List<string> alreadySelected, Random random)
private static string SelectSingleNameWithMLWithoutReplacement(List<string> candidatePool, List<string> alreadySelected, Random random)
{
if (availableNames.Count == 0) return null;
if (availableNames.Count == 1) return availableNames[0];
if (candidatePool.Count == 0) return null;
if (candidatePool.Count == 1) return candidatePool[0];
// 确保历史数据已初始化
if (historyData == null)
@@ -599,16 +626,16 @@ namespace Ink_Canvas
}
// 过滤掉已选择的人员
var candidateNames = availableNames.Where(name => !alreadySelected.Contains(name)).ToList();
if (candidateNames.Count == 0) return null;
if (candidateNames.Count == 1) return candidateNames[0];
var validCandidates = candidatePool.Where(name => !alreadySelected.Contains(name)).ToList();
if (validCandidates.Count == 0) return null;
if (validCandidates.Count == 1) return validCandidates[0];
// 检查极差:当极差达到3时,从被抽选次数最少的人中抽选
if (historyData.NameFrequency != null && historyData.NameFrequency.Count > 0)
{
// 获取所有候选人员的被抽选次数
var candidateFrequencies = new Dictionary<string, int>();
foreach (string name in candidateNames)
foreach (string name in validCandidates)
{
int count = historyData.NameFrequency.ContainsKey(name) ? historyData.NameFrequency[name] : 0;
candidateFrequencies[name] = count;
@@ -631,7 +658,7 @@ namespace Ink_Canvas
if (leastSelectedNames.Count > 0)
{
// 只从被抽选次数最少的人中随机选择
// 只从被抽选次数最少的人中不放回随机选择
int randomIndex = random.Next(0, leastSelectedNames.Count);
return leastSelectedNames[randomIndex];
}
@@ -639,10 +666,10 @@ namespace Ink_Canvas
}
}
// 获取每个人员的概率
// 获取每个候选人员的概率
var nameProbabilities = new Dictionary<string, double>();
foreach (string name in candidateNames)
foreach (string name in validCandidates)
{
// 获取基础概率
double baseProbability = GetNameProbability(name);
@@ -659,6 +686,7 @@ namespace Ink_Canvas
return ProbabilityBasedRandomSelection(nameProbabilities, random);
}
/// <summary>
/// 获取人员的概率
/// </summary>
@@ -878,30 +906,6 @@ namespace Ink_Canvas
return 1.0 - frequency;
}
/// <summary>
/// 加权随机选择(保留用于兼容,实际已改用概率选择)
/// </summary>
private static string WeightedRandomSelection(Dictionary<string, double> nameWeights, Random random)
{
if (nameWeights.Count == 0) return null;
double totalWeight = nameWeights.Values.Sum();
if (totalWeight <= 0) return nameWeights.Keys.First();
double randomValue = random.NextDouble() * totalWeight;
double currentWeight = 0;
foreach (var kvp in nameWeights)
{
currentWeight += kvp.Value;
if (randomValue <= currentWeight)
{
return kvp.Key;
}
}
return nameWeights.Keys.Last();
}
/// <summary>
/// 更新点名历史记录
+44 -19
View File
@@ -212,59 +212,84 @@ namespace Ink_Canvas
Random random = new Random();// randSeed + DateTime.Now.Millisecond / 10 % 10);
string outputString = "";
List<string> outputs = new List<string>();
List<int> rands = new List<int>();
LabelOutput2.Visibility = Visibility.Collapsed;
LabelOutput3.Visibility = Visibility.Collapsed;
new Thread(() =>
{
var animationPool = new List<int>();
for (int num = 1; num <= PeopleCount; num++)
{
animationPool.Add(num);
}
int lastDisplayedIndex = -1;
for (int i = 0; i < RandWaitingTimes; i++)
{
int rand = random.Next(1, PeopleCount + 1);
while (rands.Contains(rand))
if (animationPool.Count == 0)
{
rand = random.Next(1, PeopleCount + 1);
animationPool.Clear();
for (int num = 1; num <= PeopleCount; num++)
{
animationPool.Add(num);
}
}
rands.Add(rand);
if (rands.Count >= PeopleCount) rands = new List<int>();
int randomIndex = random.Next(0, animationPool.Count);
int selectedNumber = animationPool[randomIndex];
int lastIndex = animationPool.Count - 1;
if (randomIndex != lastIndex)
{
animationPool[randomIndex] = animationPool[lastIndex];
}
animationPool.RemoveAt(lastIndex);
Application.Current.Dispatcher.Invoke(() =>
{
if (Names.Count != 0)
{
LabelOutput.Content = Names[rand - 1];
LabelOutput.Content = Names[selectedNumber - 1];
}
else
{
LabelOutput.Content = rand.ToString();
LabelOutput.Content = selectedNumber.ToString();
}
});
Thread.Sleep(RandWaitingThreadSleepTime);
}
rands = new List<int>();
Application.Current.Dispatcher.Invoke(() =>
{
for (int i = 0; i < TotalCount; i++)
var candidatePool = new List<int>();
for (int num = 1; num <= PeopleCount; num++)
{
int rand = random.Next(1, PeopleCount + 1);
while (rands.Contains(rand))
candidatePool.Add(num);
}
for (int i = 0; i < TotalCount && candidatePool.Count > 0; i++)
{
int randomIndex = random.Next(0, candidatePool.Count);
int selectedNumber = candidatePool[randomIndex];
int lastIndex = candidatePool.Count - 1;
if (randomIndex != lastIndex)
{
rand = random.Next(1, PeopleCount + 1);
candidatePool[randomIndex] = candidatePool[lastIndex];
}
rands.Add(rand);
if (rands.Count >= PeopleCount) rands = new List<int>();
candidatePool.RemoveAt(lastIndex);
if (Names.Count != 0)
{
outputs.Add(Names[rand - 1]);
outputString += Names[rand - 1] + Environment.NewLine;
outputs.Add(Names[selectedNumber - 1]);
outputString += Names[selectedNumber - 1] + Environment.NewLine;
}
else
{
outputs.Add(rand.ToString());
outputString += rand + Environment.NewLine;
outputs.Add(selectedNumber.ToString());
outputString += selectedNumber + Environment.NewLine;
}
}
if (TotalCount <= 5)