Vereinsmeisterschaften  22aa7800eae54b428d40e835886cefe1fdefdfdf
This is a software that can be used to manage the internal competition of the swimming club Illertissen called "Vereinsmeisterschaften".
Loading...
Searching...
No Matches
RacesVariantsGenerator.cs
1using System.Collections.Concurrent;
4
6{
12 {
13 private readonly Random _random = new();
14 private readonly double _minScoreThreshold;
15 private readonly int _maxGroupSize;
16 private readonly double _maxOneElementGroupPercentage;
17 private readonly IProgress<double> _progressIteration;
18 private readonly IProgress<double> _progressSolution;
19 private readonly int _requiredVariantsCount;
20 private readonly int _maxIterations;
21
32 public RacesVariantsGenerator(IProgress<double> progressIteration = null, IProgress<double> progressSolution = null, int requiredVariantsCount = 100, int maxIterations = 100000, double minScoreThreshold = 90, int maxGroupSize = 3, double maxOneElementGroupPercentage = 0.15)
33 {
34 _progressIteration = progressIteration;
35 _progressSolution = progressSolution;
36 _maxGroupSize = maxGroupSize;
37 _minScoreThreshold = minScoreThreshold;
38 _maxOneElementGroupPercentage = maxOneElementGroupPercentage;
39 _requiredVariantsCount = requiredVariantsCount;
40 _maxIterations = maxIterations;
41 }
42
50 public async Task<List<RacesVariant>> GenerateBestRacesAsync(List<List<PersonStart>> sets, IWorkspaceService workspaceService = null, CancellationToken cancellationToken = default)
51 {
52 ConcurrentBag<RacesVariant> bestRaces = new ConcurrentBag<RacesVariant>();
53 int attempts = 0;
54 int foundVariants = 0;
55
56 await Task.Run(() =>
57 {
58 Parallel.For(0, _maxIterations, new ParallelOptions() { MaxDegreeOfParallelism = (int)(0.25 * Environment.ProcessorCount) }, (i, state) =>
59 {
60 if (cancellationToken.IsCancellationRequested || bestRaces.Count >= _requiredVariantsCount)
61 {
62 state.Stop();
63 return;
64 }
65
66 // Random grouping creation
67 RacesVariant candidate = CreateValidGrouping(sets, workspaceService);
68
69 // Calculate score and keep if above threshold
70 double score = candidate.CalculateScore();
71 if (score >= _minScoreThreshold)
72 {
73 bestRaces.Add(candidate);
74 Interlocked.Increment(ref foundVariants);
75 }
76
77 Interlocked.Increment(ref attempts);
78
79 // Calculate progress as a mixture of iteration progress and solution progress
80 double iterationProgress = (double)attempts / _maxIterations;
81 double solutionProgress = (double)foundVariants / _requiredVariantsCount;
82 //double overallProgress = Math.Min(1.0, Math.Max(iterationProgress, solutionProgress));
83
84 _progressIteration?.Report(iterationProgress * 100);
85 _progressSolution?.Report(solutionProgress * 100);
86 });
87 }, cancellationToken);
88
89 return bestRaces.OrderByDescending(r => r.Score).Take(_requiredVariantsCount).ToList();
90 }
91
98 private RacesVariant CreateValidGrouping(List<List<PersonStart>> sets, IWorkspaceService workspaceService = null)
99 {
100 List<List<PersonStart>> groups = new List<List<PersonStart>>();
101 List<List<PersonStart>> shuffledSets = sets.OrderBy(_ => _random.Next()).ToList(); // Shuffle sets for more randomness
102 int oneElementGroupsCount = 0;
103
104 foreach (List<PersonStart> set in shuffledSets)
105 {
106 HashSet<PersonStart> remainingElements = new HashSet<PersonStart>(set.OrderBy(_ => _random.Next())); // Shuffle each set
107
108 while (remainingElements.Count > 0)
109 {
110 int maxSize = Math.Min(_maxGroupSize, remainingElements.Count);
111 int minSize = Math.Max(2, (int)(maxSize * 0.5)); // Avoid too small groups
112
113 int groupSize = _random.Next(minSize, maxSize + 1);
114 List<PersonStart> selectedGroup = remainingElements.Take(groupSize).ToList();
115
116 if (selectedGroup.Count == 1)
117 {
118 oneElementGroupsCount++;
119 }
120
121 groups.Add(selectedGroup);
122 foreach (PersonStart item in selectedGroup)
123 remainingElements.Remove(item);
124 }
125 }
126
127 // If too many single element groups were created → Adjust instead of restart
128 if ((double)oneElementGroupsCount / groups.Count > _maxOneElementGroupPercentage)
129 {
130 groups = MergeSmallGroups(groups, sets);
131 }
132
133 RacesVariant variant = new RacesVariant(workspaceService);
134 foreach (var group in groups)
135 {
136 variant.Races.Add(new Race(group));
137 }
138
139 return variant;
140 }
141
148 private List<List<PersonStart>> MergeSmallGroups(List<List<PersonStart>> groups, List<List<PersonStart>> sets)
149 {
150 List<List<PersonStart>> result = new();
151 HashSet<int> mergedIndices = new();
152
153 for (int i = 0; i < groups.Count; i++)
154 {
155 if (mergedIndices.Contains(i))
156 continue;
157
158 var groupA = groups[i];
159
160 // Nur kleine Gruppen betrachten
161 if (groupA.Count >= 1)
162 {
163 result.Add(groupA);
164 continue;
165 }
166
167 bool merged = false;
168
169 for (int j = i + 1; j < groups.Count; j++)
170 {
171 if (mergedIndices.Contains(j))
172 continue;
173
174 var groupB = groups[j];
175
176 if (groupA.Count + groupB.Count <= _maxGroupSize &&
177 AreFromSameSet(groupA.Concat(groupB).ToList(), sets))
178 {
179 result.Add(groupA.Concat(groupB).ToList());
180 mergedIndices.Add(i);
181 mergedIndices.Add(j);
182 merged = true;
183 break;
184 }
185 }
186
187 if (!merged)
188 {
189 result.Add(groupA);
190 }
191 }
192
193 return result;
194 }
195
196 private bool AreFromSameSet(List<PersonStart> starts, List<List<PersonStart>> sets)
197 {
198 foreach (var set in sets)
199 {
200 if (starts.All(p => set.Contains(p)))
201 return true;
202 }
203 return false;
204 }
205 }
206}
Class describing a start of a person.
Definition PersonStart.cs:9
Class that represents a single race.
Definition Race.cs:12
Class that represents a combination variant of all single races.
double CalculateScore()
Recalculate all scores.
RacesVariant CreateValidGrouping(List< List< PersonStart > > sets, IWorkspaceService workspaceService=null)
Create a valid group by combining the elements within the sets randomly.
RacesVariantsGenerator(IProgress< double > progressIteration=null, IProgress< double > progressSolution=null, int requiredVariantsCount=100, int maxIterations=100000, double minScoreThreshold=90, int maxGroupSize=3, double maxOneElementGroupPercentage=0.15)
Create a new instance of RacesVariantsGenerator.
List< List< PersonStart > > MergeSmallGroups(List< List< PersonStart > > groups, List< List< PersonStart > > sets)
Merge small groups to reduce the number of single element groups.
async Task< List< RacesVariant > > GenerateBestRacesAsync(List< List< PersonStart > > sets, IWorkspaceService workspaceService=null, CancellationToken cancellationToken=default)
Calculate the variants asynchronously.
Interface for a service used to manage a workspace.