Beispiel #1
0
        public bool CompareTo(LvCombination combination)
        {
            //Checking if parameters values are valid
            if (Combination == null || combination == null || combination.Combination == null)
            {
                throw new ArgumentException();
            }

            //If number of groups isn't the same for both records
            if (Combination.Count != combination.Combination.Count)
            {
                return(false);
            }

            //Going trough each group in record 1, and checking if that group also exists in record 2
            var tempCombination = new List <List <Student> >(combination.Combination);

            for (int i = 0; i < Combination.Count; i++)
            {
                var group = tempCombination.FirstOrDefault(r => Combination[i].Count == r.Count && Combination[i].Except(r).Any() == false);
                if (group == null)
                {
                    return(false);
                }
                tempCombination.Remove(group);
            }
            return(true);
        }
Beispiel #2
0
        private LvCombination GetBestLvCombination(List <LvCombination> combinations, int currentLvIndex, IProgress <double> progressPercentage, IProgress <TimeSpan> progressTimeLeft)
        {
            LvCombination bestCombination  = null;
            bool          firstCombination = true;
            int           bestCombinationMaxSittingCount            = 0;
            int           bestCombinationStudentSittingDiff         = 0;
            int           bestCombinationMinMaxSum                  = 0;
            int           bestCombinationMinSittingCount            = 0;
            int           bestCombinationMinAndMaxSittingCountCount = 0;
            int           bestCombinationGroupRepetitionCount       = 0;
            int           loopCount                 = 0;
            var           timeEstimator             = new TimeLeftEstimator();
            var           combinationsLoopStopwatch = new Stopwatch();

            combinationsLoopStopwatch.Start();
            foreach (var combination in combinations)
            {
                _cancellationSource?.Token.ThrowIfCancellationRequested();

                UpdateStudentHistory(combination, true);
                var  studentSittingHistoryValues = GetStudentSittingHistoryValues().ToList();
                bool isBestCombination           = firstCombination || IsBestCombination(combination, studentSittingHistoryValues, bestCombinationMaxSittingCount, bestCombinationMinSittingCount, bestCombinationStudentSittingDiff, bestCombinationMinMaxSum, bestCombinationMinAndMaxSittingCountCount, bestCombinationGroupRepetitionCount);

                //Updating variables if this is the best combination so far
                if (isBestCombination)
                {
                    //Variables are being populated using new data since some of the variables in this foreach loop might not have been updated (since that depends on what criteria the best combination was selected)
                    bestCombination = combination;
                    if (studentSittingHistoryValues.Count != 0)
                    {
                        bestCombinationMaxSittingCount            = studentSittingHistoryValues.Max();
                        bestCombinationMinSittingCount            = studentSittingHistoryValues.Min();
                        bestCombinationStudentSittingDiff         = GetStudentSittingDiff(combination);
                        bestCombinationMinMaxSum                  = GetMinMaxValues(combination).Sum();
                        bestCombinationGroupRepetitionCount       = GetGroupRepetitionCount(combination);
                        bestCombinationMinAndMaxSittingCountCount = GetMinAndMaxSittingCountCount(studentSittingHistoryValues);
                        firstCombination = false;
                    }
                }

                UpdateStudentHistory(combination, false);

                loopCount++;
                if (loopCount % 1000 == 0 || loopCount == combinations.Count)
                {
                    combinationsLoopStopwatch.Stop();
                    timeEstimator?.AddTime(new TimeSpan(combinationsLoopStopwatch.ElapsedTicks));
                    var timeLeft = timeEstimator.GetTimeLeft((combinations.Count - loopCount) / 1000 + (_lvCount - currentLvIndex - 1) * combinations.Count / 1000);
                    progressTimeLeft?.Report(timeLeft);
                    progressPercentage?.Report((double)((currentLvIndex + (double)loopCount / combinations.Count) / _lvCount));
                    combinationsLoopStopwatch.Restart();
                }
            }
            return(bestCombination);
        }
Beispiel #3
0
        private int GetGroupRepetitionCount(LvCombination combination)
        {
            int count = 0;

            foreach (var group in combination.Combination)
            {
                foreach (var _combination in ShuffleResult)
                {
                    foreach (var _group in _combination.Combination)
                    {
                        if (group.Count == _group.Count && group.Except(_group).Any() == false)
                        {
                            count++;
                        }
                    }
                }
            }
            return(count);
        }
Beispiel #4
0
 private IEnumerable <int> GetMinMaxValues(LvCombination combination)
 {
     foreach (var group in combination.Combination)
     {
         foreach (var student in group)
         {
             int minMax = 0;
             if (student.StudentSittingHistory.Count != 0)
             {
                 minMax = student.StudentSittingHistory.Values.Max();
                 if (student.StudentSittingHistory.Count == Students.Count - 1)
                 {
                     minMax -= student.StudentSittingHistory.Values.Min();
                 }
             }
             yield return(minMax);
         }
     }
 }
Beispiel #5
0
 private static void UpdateStudentHistory(LvCombination combination, bool increment)
 {
     foreach (var groupCombination in combination.Combination)
     {
         foreach (var student in groupCombination)
         {
             foreach (var student2 in groupCombination)
             {
                 if (student == student2)
                 {
                     continue;
                 }
                 if (student.StudentSittingHistory.ContainsKey(student2) == false)
                 {
                     student.StudentSittingHistory.Add(student2, 0);
                 }
                 student.StudentSittingHistory[student2] += (increment) ? 1 : -1;
             }
         }
     }
 }
Beispiel #6
0
        private static int GetStudentSittingDiff(LvCombination combination)
        {
            int  minCount     = 0;
            int  maxCount     = 0;
            bool firstStudent = true;

            foreach (var groupCombination in combination.Combination)
            {
                foreach (var student in groupCombination)
                {
                    int sittingCount = student.StudentSittingHistory.Values.Sum();
                    if (sittingCount > maxCount)
                    {
                        maxCount = sittingCount;
                    }
                    if (sittingCount < minCount || firstStudent)
                    {
                        minCount     = sittingCount;
                        firstStudent = false;
                    }
                }
            }
            return(maxCount - minCount);
        }
Beispiel #7
0
        /// <summary>
        /// This method returns all student sitting combinations for a lv
        /// </summary>
        /// <param name="takeOffset">Offset of the number which will be taken. For example, if every second combination is taken and offset is 3, following combinations will be returned: 5th, 7th, 9th, 11th...</param>
        /// <param name="takeEvery">Specifies which combinations will be returned. If there are 1000 combinations and <paramref name="maxCombinationCount"/> is 100, this parameter will be 10. That means that 10th, 20th, 30th etc. combinations will be returned</param>
        /// <returns></returns>
        private IEnumerable <LvCombination> GetLvCombinations(List <int> groupSizes, List <Student> students, double takeEvery = 0, double takeOffset = 0)
        {
            bool allGroupsAreSameSize = groupSizes.Distinct().Count() == 1;
            var  sameGroupSizes       = groupSizes.Where(s => s == groupSizes[0]).ToList();

            //Setting takeEvery value if this is the first time this method was called
            if (takeEvery == 0 && takeOffset == 0)
            {
                takeEvery = (_maxCombinationCount <= 0) ? -1 : (double)new LvCombinationCountCalculator(groupSizes, groupSizes.Sum()).GetLvCombinationCount() / _maxCombinationCount;
                takeEvery = (takeEvery <= 1) ? -1 : takeEvery;
            }

            //Going trough each combination for the first group and getting all possible combinations for other groups
            Student currentFirstCombinationNumber = null;
            var     availableStudents             = new List <Student>(students);

            foreach (var firstGroupCombination in GetLvCombinationsForFirstGroup(groupSizes, students))
            {
                //Returning first group combination if there is only 1 number group
                if (groupSizes.Count == 1)
                {
                    //Skipping the combination if necessary
                    if (takeOffset > 0)
                    {
                        takeOffset--;
                        continue;
                    }

                    takeOffset += takeEvery - 1;
                    yield return(new LvCombination(firstGroupCombination.Combination));

                    continue;
                }

                //Removing some available students if the first student in the combination changed
                if (sameGroupSizes.Count > 1)
                {
                    var firstCombinationNumber = firstGroupCombination.Combination[0][0];
                    if (firstCombinationNumber != currentFirstCombinationNumber)
                    {
                        currentFirstCombinationNumber = firstCombinationNumber;
                        availableStudents.Remove(firstCombinationNumber);
                    }
                }

                //Skipping the combination if all combinations in this group would be skipped
                ulong combinationCount = GetCombinationCount(groupSizes, availableStudents);
                if (takeOffset - combinationCount > 0)
                {
                    takeOffset -= combinationCount;
                    continue;
                }

                //Getting all possible combinations for groups that have the same size as the first group
                if (sameGroupSizes.Count > 1)
                {
                    var tempAvailableStudents = availableStudents.Except(firstGroupCombination.Combination[0]).ToList();
                    var tempGroupSizes        = new List <int>(sameGroupSizes);
                    tempGroupSizes.RemoveAt(0);

                    var sameSizeCombinations = GetLvCombinations(tempGroupSizes, tempAvailableStudents, takeEvery, takeOffset);
                    foreach (var sameSizeCombination in sameSizeCombinations)
                    {
                        var lvCombination = new LvCombination(new List <List <Student> >(firstGroupCombination.Combination));
                        lvCombination.Combination.AddRange(sameSizeCombination.Combination);

                        //Returning the combination if all groups have the same size
                        if (allGroupsAreSameSize)
                        {
                            takeOffset += takeEvery;
                            yield return(lvCombination);

                            continue;
                        }

                        //Getting all possible combinations for the groups that don't have the same size as the first group
                        tempAvailableStudents = new List <Student>(students);
                        tempAvailableStudents.RemoveAll(i => firstGroupCombination.Combination[0].Contains(i));
                        sameSizeCombination.Combination.ForEach(g => tempAvailableStudents.RemoveAll(i => g.Contains(i)));
                        tempGroupSizes = groupSizes.Except(sameGroupSizes).ToList();
                        foreach (var otherGroupsCombination in GetLvCombinations(tempGroupSizes, tempAvailableStudents, takeEvery, takeOffset))
                        {
                            //Combining all of the group combinations into 1 combination
                            otherGroupsCombination.Combination.InsertRange(0, lvCombination.Combination);
                            takeOffset += takeEvery;
                            yield return(otherGroupsCombination);
                        }
                    }
                }
                else
                {
                    //Getting number combinations for other groups
                    var tempAvailableStudents = availableStudents.Except(firstGroupCombination.Combination[0]).ToList();
                    var tempGroupSizes        = groupSizes.Where(s => s != groupSizes[0]).ToList();
                    foreach (var c in GetLvCombinations(tempGroupSizes, tempAvailableStudents, takeEvery, takeOffset))
                    {
                        //Combining all of the group combinations into 1 combination
                        c.Combination.Insert(0, firstGroupCombination.Combination[0]);
                        takeOffset += takeEvery;
                        yield return(c);
                    }
                }
                takeOffset -= combinationCount;
            }
        }
Beispiel #8
0
        /// <summary>
        /// This method is used to create best student sitting combinations based on the fields in this class
        /// </summary>
        /// <param name="progressPercentage">Completed percentage for the shuffle step which is being executed at that moment</param>
        /// <param name="progressText">Text containing data about shuffle step which is being executed at that moment</param>
        /// <param name="progressTimeLeft">Estimated time left for shuffle step which is being executed at that moment to finish</param>
        /// <returns></returns>
        public void Shuffle(IProgress <double> progressPercentage = null, IProgress <string> progressText = null, IProgress <TimeSpan> progressTimeLeft = null)
        {
            /*BEST COMBINATION ALGORITHM EXPLANATION:
             * An algorithm which calculates best combination takes 6 parameters into consideration:
             *
             * 1st (most important) parameter is maxSittingCount:
             * max amount of times a student sat with with another student.
             * Lower amount is better.
             *
             * 2nd parameter is minSittingCount:
             * min amount of times a student sat with another student.
             * Higher is better.
             *
             * 3rd parameter is studentSittingDiff:
             * (highest sum of student sitting values for a student) - (lowest sum of student sitting value for a student (doesn't need to be the same student)).
             * Lower number is better since that forces the students to sit in groups of various sizes, instead of alwaays sitting in the biggest/smalled group.
             *
             * 4th parameter is minMaxSum:
             * sum of (max amount of times a student sat with another student) - (min amount of times that same student student sat with another student) for all students.
             * Lower is better.
             *
             * 5th parameter is minAndMaxSittingCountCount:
             * count of student sitting count values (student sitting count values - amount of times students sat with each other) where student sitting count is min or max.
             * Lower is better since it means that the deviation from average student sitting count is lower.
             *
             * 6th (least important) parameter is groupRepetitionCount:
             * Sum of amount of times each group in the combination was used in previous combinations
             *
             * Algorithm is pretty much doing
             * lvCombinations.OrderBy(1st parameter).OrderBy(2nd parameter).OrderBy(3nd parameter).OrderBy(4th parameter).First(),
             * but doing it manually since progress could otherwise only be updated once the best combination is found (which can take a lot of time)*/

            //Setup
            foreach (var student in Students)
            {
                student.StudentSittingHistory.Clear();
            }
            _shuffleResult.Clear();
            progressPercentage?.Report(0);
            progressText?.Report("Računanje svih kombinacija sjedenja");
            progressTimeLeft?.Report(new TimeSpan(0));

            ulong combinationCount = new LvCombinationCountCalculator(_groups.Select(g => g.Size).ToList(), _students.Count).GetLvCombinationCount();

            if (combinationCount > (ulong)MaxCombinationCount)
            {
                combinationCount = (ulong)MaxCombinationCount;
            }

            //Going trough each LV
            var combinations = GetLvCombinations(progressPercentage, progressTimeLeft, combinationCount);

            progressPercentage?.Report(0);
            progressText?.Report("Rasporeda se stvara");
            progressTimeLeft?.Report(new TimeSpan(0));
            for (int lv = 0; lv < _lvCount; lv++)
            {
                //Getting best student sitting combination for current lv
                LvCombination bestCombination = GetBestLvCombination(combinations, lv, progressPercentage, progressTimeLeft);

                //Updating shuffle result and progress
                UpdateStudentHistory(bestCombination, true);
                _shuffleResult.Add(bestCombination);

                //If every student sat with other students the same amount of times, there is no need to do further calculations since other lv's would just be repeats of current student sitting combinations
                if (GetStudentSittingHistoryValues().Distinct().Count() <= 1)
                {
                    break;
                }
            }
            progressTimeLeft?.Report(new TimeSpan(0));

            //DEBUGGING OUTPUT: used for testing purposes
            Debug_PrintResult();
        }
Beispiel #9
0
        private bool IsBestCombination(LvCombination combination, List <int> studentSittingHistoryValues, int bestCombinationMaxSittingCount, int bestCombinationMinSittingCount, int bestCombinationStudentSittingDiff, int bestCombinationMinMaxSum, int bestCombinationMinAndMaxSittingCountCount, int bestCombinationGroupRepetitionCount)
        {
            //Checking if max sitting count is better
            int maxSittingCount = studentSittingHistoryValues.Max();

            if (maxSittingCount < bestCombinationMaxSittingCount)
            {
                return(true);
            }
            if (maxSittingCount > bestCombinationMaxSittingCount)
            {
                return(false);
            }

            //Checking if min sitting count is better
            int minSittingCount = studentSittingHistoryValues.Min();

            if (minSittingCount > bestCombinationMinSittingCount)
            {
                return(true);
            }
            else if (minSittingCount < bestCombinationMinSittingCount)
            {
                return(false);
            }

            //Checking if min student sitting diff is better
            int studentSittingDiff = GetStudentSittingDiff(combination);

            if (studentSittingDiff < bestCombinationStudentSittingDiff)
            {
                return(true);
            }
            else if (studentSittingDiff > bestCombinationStudentSittingDiff)
            {
                return(false);
            }

            //Checking if minMaxDiff is better
            int minMaxSum = GetMinMaxValues(combination).Sum();

            if (minMaxSum < bestCombinationMinMaxSum)
            {
                return(true);
            }
            else if (minMaxSum > bestCombinationMinMaxSum)
            {
                return(false);
            }

            //Checking if minMinAndMax sitting count is better or if min sitting count is higher or if max sitting count is lower
            int minAndMaxSittingCountCount = GetMinAndMaxSittingCountCount(studentSittingHistoryValues);

            if (bestCombinationMinAndMaxSittingCountCount < minAndMaxSittingCountCount)
            {
                return(true);
            }
            else if (bestCombinationMinAndMaxSittingCountCount > minAndMaxSittingCountCount)
            {
                return(false);
            }

            //Checking is group repetition count is better
            int groupRepetitionCount = GetGroupRepetitionCount(combination);

            if (groupRepetitionCount < bestCombinationGroupRepetitionCount)
            {
                return(true);
            }
            return(false);
        }