/// <summary> /// Generates a new seed. /// </summary> public int GenerateSeed(UserQuestionData userQuestionData, int numSeeds) { var existingSeeds = userQuestionData.Submissions ?.Where(uqs => uqs.Seed.HasValue && uqs.Seed < numSeeds) ?.GroupBy(uqs => uqs.Seed.Value) ?.ToDictionary(g => g.Key, g => g.Count()) ?? new Dictionary <int, int>(); int newSeed; if (existingSeeds.Count >= numSeeds) { newSeed = existingSeeds .MinBy(kvp => kvp.Value) .Key; } else { do { newSeed = _randomNumberProvider.NextInt() % numSeeds; } while (existingSeeds.ContainsKey(newSeed)); } return(newSeed); }
/// <summary> /// Returns the next question ID to use. /// </summary> public int GetNextQuestionId( UserQuestionData userQuestionData, IList <int> availableQuestionIds) { var seenQuestionIds = userQuestionData ?.Submissions ?.Where(uqs => uqs.Seed.HasValue) ?.GroupBy(uqs => uqs.Seed.Value) ?.ToDictionary(g => g.Key, g => g.Count()) ?? new Dictionary <int, int>(); var validQuestionIds = new HashSet <int>(availableQuestionIds); var unseenQuestionIds = validQuestionIds .Except(seenQuestionIds.Keys) .ToList(); if (unseenQuestionIds.Any()) { var randomIndex = _randomNumberProvider.NextInt() % unseenQuestionIds.Count; return(unseenQuestionIds[randomIndex]); } else { return(seenQuestionIds .Where(kvp => validQuestionIds.Contains(kvp.Key)) .MinBy(kvp => kvp.Value) .Key); } }
public Dictionary <Student, Project> AllocateProjects(State state) { if (state.Students.Count > state.Supervisors.Sum(supervisor => supervisor.Capacity)) { throw new ArgumentException($"Not enough capacity for the student list provided. Capacity = '{state.Supervisors.Sum(supervisor => supervisor.Capacity)}', Student count = '{state.Students.Count}'", nameof(state)); } // Assign based on preferences. Highest GPA -> lowest. GPA clashes resolved by random number var studentsByGpa = state.Students .Where(student => student.ProjectInterests.Count > 0) .OrderByDescending(student => student.Gpa) .ThenBy(student => _randomNumberProvider.NextInt(state.Students.Count)); _logger.LogInformation("Assigning students to projects based on their preferences, ordered by GPA."); foreach (var student in studentsByGpa) { _logger.LogTrace("Trying to assign a project to student '{0}'", student.Id); var project = student.ProjectInterests.FirstOrDefault(projectInterest => projectInterest.HasSpaceRemaining); if (project == null) { _logger.LogInformation("None of student '{0}'s preferences have space remaining. Skipping for now.", student.Id); continue; } _logger.LogTrace("Student '{0}'s preference #{1} is still available. Assigning...", student.Id, student.ProjectInterests.IndexOf(project) + 1); _studentRepository.AssignProject(student, project); } _logger.LogInformation("All student preferences assigned (where available)"); // Randomly assign remaining students var projectsLeft = state.Projects .Where(project => project.HasSpaceRemaining) .ToList(); var studentsLeft = state.Students .Where(student => !student.HasProject) .ToList(); _logger.LogInformation("Assigning remaining {0} students a project at random.", studentsLeft.Count); foreach (var student in studentsLeft) { var project = projectsLeft[_randomNumberProvider.NextInt(projectsLeft.Count)]; _studentRepository.AssignProject(student, project); if (!project.HasSpaceRemaining) { projectsLeft.Remove(project); } if (!project.Supervisor.HasSpaceRemaining) { foreach (var supervisorProject in project.Supervisor.Projects) { if (projectsLeft.Contains(supervisorProject)) { projectsLeft.Remove(supervisorProject); } } } } return(state.Students.ToDictionary( student => student, student => student.Project )); }