public override int CalculateLocalCost(IAllel[] rowOfAlleles, int index) { string doctor = _doctorIds[index]; DutyRequirementForMonthDto dutyRequirement = _dutyRequirementsForMonth.FirstOrDefault(d => d.UserId == doctor); if (dutyRequirement == null) { return(0); } int numberOfRequiredDuties = dutyRequirement.RequiredTotalDutiesInMonth; int numberOfAssignedDuties = rowOfAlleles.Count(a => a != null && a.Value == Allel.OnDuty.Value); return(numberOfRequiredDuties - numberOfAssignedDuties); }
private IEnumerable <int> MatrixAssignedDutyRequirements(IAllel[,] dna) { for (int i = 0; i < dna.GetLength(0); i++) { string doctor = _doctorIds[i]; DutyRequirementForMonthDto dutyRequirement = _dutyRequirementsForMonth .FirstOrDefault(d => d.UserId == doctor); if (dutyRequirement == null) { continue; } for (int j = 0; j < dna.GetLength(1); j++) { if (dna[i, j] != null && dna[i, j].Value == Allel.OnDuty.Value) { yield return(1); } } } }
private void SetDuties() { Parallel.ForEach(_individuals, (individual) => { List <DutyRequirementForMonthDto> dutyRequirementsForMonth = new List <DutyRequirementForMonthDto>(_dutyRequirementsForMonth); List <string> doctorsAvailableForSchedule = new List <string>(_doctorIds); for (int i = 0; i < DateTime.DaysInMonth(_date.Year, _date.Month); i++) { // 1. Usuwamy z listy doctorsAvailableForSchedule lekarzy, którzy wyczerpali dostępną ilość dyżurów foreach (var doctorId in new List <string>(doctorsAvailableForSchedule)) { var doctorDutyRequirements = dutyRequirementsForMonth.FirstOrDefault(dr => dr.UserId == doctorId); if (doctorDutyRequirements != null) { if (doctorDutyRequirements.RequiredTotalDutiesInMonth == 0) { doctorsAvailableForSchedule.Remove(doctorId); } } } // 2. Jeżeli nadal na liście doctorsAvailableForSchedule jest dostępny lekarz kontunuujemy if (doctorsAvailableForSchedule.Count > 0) { // 2. Tworzymy z listy doctorsAvailableForSchedule początkową pulę lekarzy dostępnych danego dnia (doctorsAvailableForDay) List <string> doctorsAvailableForDay = new List <string>(doctorsAvailableForSchedule); // 3. Usuwamy lekarzy, którzy zgłosili niemożność lub mieli dyżur pooprzedniego dnia for (int j = i; j < i + 1; j++) { for (int k = 0; k < _totalNumberOfDoctors; k++) { for (int m = j; m <= j; m++) { if (individual.Dna[k, m] == Allel.DayOff) { doctorsAvailableForDay.Remove(_doctorIds[k]); continue; } // poprzedni dzień if (m > 0) { if (individual.Dna[k, m - 1] == Allel.OnDuty) { doctorsAvailableForDay.Remove(_doctorIds[k]); continue; } } // ostatni dzień poprzedniego miesiąca if (m == 0 && _doctorsOnDutyLastDayOfPreviousMonth.Count > 0) { if (_doctorsOnDutyLastDayOfPreviousMonth.Contains(_doctorIds[k])) { doctorsAvailableForDay.Remove(_doctorIds[k]); } } } } } DateTime currentDate = new DateTime(_date.Year, _date.Month, i + 1); if (doctorsAvailableForDay.Count > 0) { // 4.Jeżeli w danym dniu przypada święto, usuwamy lekarza z listy dostępnych w tym dniu jeśli lekarz wyczerpał limit dostępności w święta(Item3) if (IsWeekendOrPublicHoliday(currentDate)) { foreach (var doctorId in new List <string>(doctorsAvailableForDay)) { var doctorDutyRequirements = dutyRequirementsForMonth.FirstOrDefault(dr => dr.UserId == doctorId); if (doctorDutyRequirements != null) { if (doctorDutyRequirements.RequiredTotalHolidayDuties == 0) { doctorsAvailableForDay.Remove(doctorId); } } } } // 5. Jeżeli dzień nie jest świąteczny, usuwamy lekarza z listy dostępnych w tym dniu jeśi lekarz wyczerpał limit dostepności w dni robocze (Item2) if (!IsWeekendOrPublicHoliday(currentDate)) { foreach (var doctorId in new List <string>(doctorsAvailableForDay)) { var doctorDutyRequirements = dutyRequirementsForMonth.FirstOrDefault(dr => dr.UserId == doctorId); if (doctorDutyRequirements != null) { if (doctorDutyRequirements.RequiredTotalWeekdayDuties == 0) { doctorsAvailableForDay.Remove(doctorId); } } } } // 6. Dodajemy lekarzy na dyżur. Odniesieniem jest indeks na liście _doctorsId i wiersze tablicy for (int t = 0; t < _numberOfDoctorsOnDuty; t++) { if (doctorsAvailableForDay.Count > 0) { var doctor = doctorsAvailableForDay[ RandomGenerator.IntBetween(0, doctorsAvailableForDay.Count)]; int index = _doctorIds.IndexOf(doctor); individual.Dna[index, i] = Allel.OnDuty; var doctorDutyRequirements = dutyRequirementsForMonth.FirstOrDefault(d => d.UserId == doctor); if (doctorDutyRequirements != null) { int requiredTotalDuties; int requiredWeekdayDuties; int requiredHolidayDuties; if (IsWeekendOrPublicHoliday(currentDate)) { requiredTotalDuties = doctorDutyRequirements.RequiredTotalDutiesInMonth - 1; requiredWeekdayDuties = doctorDutyRequirements.RequiredTotalWeekdayDuties; requiredHolidayDuties = doctorDutyRequirements.RequiredTotalHolidayDuties - 1; } else { requiredTotalDuties = doctorDutyRequirements.RequiredTotalDutiesInMonth - 1; requiredWeekdayDuties = doctorDutyRequirements.RequiredTotalWeekdayDuties - 1; requiredHolidayDuties = doctorDutyRequirements.RequiredTotalHolidayDuties; } DutyRequirementForMonthDto updated = new DutyRequirementForMonthDto { UserId = doctorDutyRequirements.UserId, RequiredTotalDutiesInMonth = requiredTotalDuties, RequiredTotalWeekdayDuties = requiredWeekdayDuties, RequiredTotalHolidayDuties = requiredHolidayDuties }; dutyRequirementsForMonth.Remove(doctorDutyRequirements); dutyRequirementsForMonth.Add(updated); } doctorsAvailableForDay.Remove(doctor); } else { break; } } } } else { // 7. Jeżeli nie ma lekarzy z dostępnymi dniami dyżurowymi w grafiku (doctorsAvailableForScheduler) przerywamy pętlę break; } } }); }
public IAllel[,] Repair(IAllel[,] dna) { List <int> rows = new List <int>(); // 1. Usuwamy zbędne allele jeśli w kolumnie istnieje więcej niż liczba lekarzy wymaganych na dyżurze // a) zliczamy allel OnDuty i rząd zapisujemy na liście // TODO: Usuwane allele mogą być tymi, których ilość została na sztywno zdefiniowana przez lekarza for (int i = 0; i < DateTime.DaysInMonth(_date.Year, _date.Month); i++) { for (int j = 0; j < _totalNumberOfDoctors; j++) { for (int k = i; k <= i; k++) { if (dna[j, k] != null && dna[j, k].Value == Allel.OnDuty.Value) { rows.Add(j); } } } // b. jeśli w danym dniu jest więcej alleli OnDuty niż powinno być usuwamy zbędne if (rows.Count > _numberOfDoctorsOnDuty) { RemoveRedundantAlleles(dna, i, rows); } rows.Clear(); } // 2. Jeśli w kolumnie jest mniej lekarzy niż powinno być dopełniamy do wymaganej liczby /* * Niedostępności: * - kiedy ma już przypisany dyżur, * - kiedy jest wstawiona niemożność, * - kiedy ma dyżur następnego dnia, * - kiedy miał dyżur poprzedniego dnia, * - kiedy miał dyżur ostatniego dnia w poprzednim miesiącu (przypadek szczególny dla dyżurów 1 dnia) * */ List <int> unavailableDoctors = new List <int>(); for (int i = 0; i < DateTime.DaysInMonth(_date.Year, _date.Month); i++) { int onDutyCount = 0; // zliczamy ile jest na dyżurze for (int j = 0; j < _totalNumberOfDoctors; j++) { for (int k = i; k <= i; k++) { if (dna[j, k] != null && dna[j, k].Value == Allel.OnDuty.Value) { onDutyCount++; rows.Add(j); // niedostępny jeśli ma juz przypisany dużur } if (dna[j, k] != null && dna[j, k].Value == Allel.DayOff.Value) { rows.Add(j); // niedostępny jeśli jest "niemożność" } if (k < (DateTime.DaysInMonth(_date.Year, _date.Month) - 1)) { if (dna[j, k + 1] != null && dna[j, k + 1].Value == Allel.OnDuty.Value) { rows.Add(j); // niedostępny jeśli ma dyżur następnego dnia } } if (k > 0) { if (dna[j, k - 1] != null && dna[j, k - 1].Value == Allel.OnDuty.Value) { rows.Add(j); // niedostępny niedostępny jeśli miał dyżur poprzedniego dnia } } if (k == 0) { foreach (var doctor in _doctorsOnDutyLastDayOfPreviousMonth) { int index = _doctorIds.IndexOf(doctor); rows.Add(index); // niedostępny - przypadek szczególny - dużur poprzedniego dnia w poprzednim miesiącu } } // spawdzamy czy możemy dodać lekarza ze względu na limity, które zdefiniował /* * Ponieważ istnieje korelacja pomiędzy indeksami listy "doctorIds" oraz indeksami wierszy w tablicy, * możliwe jest wyciągniecie GuidId lekarza z listy doctorIds i sprawdzenie jego wymagań dyżurowych */ // if(unavailableDoctors.Contains(j) --> nie wykonuj kodu poniżej, wiemy, że już jest niedostępny if (!unavailableDoctors.Contains(j)) { string doctorId = _doctorIds[j]; DutyRequirementForMonthDto doctorDutyRequirement = _dutyRequirementsForMonth.FirstOrDefault(d => d.UserId == doctorId); // jeżeli sformułowano wymagania dotyczące ilości dyżurów if (doctorDutyRequirement != null) { var numberOfDutiesInTheRow = dna.GetRow(j) .Count(a => a != null && a.Value == Allel.OnDuty.Value); if (numberOfDutiesInTheRow >= doctorDutyRequirement.RequiredTotalDutiesInMonth) { //rows.Add(j); // niesdostępny - wyczerpał zdefiniowany limit unavailableDoctors.Add(j); // dodajemy do listy niedostępnych } else { DateTime currentDate = new DateTime(_date.Year, _date.Month, k + 1); // jeśli dzień jest świętem sprawdzamy czy limit świąt został wyczerpany if (IsWeekendOrPublicHoliday(currentDate)) { var doctorDuties = dna.GetRow(j); int holidayDutiesCount = 0; // liczymy ile dyżurów ma w święta for (int d = 0; d < doctorDuties.Length; d++) { if (IsWeekendOrPublicHoliday(new DateTime(_date.Year, _date.Month, d + 1)) && doctorDuties[d] != null && doctorDuties[d].Value == Allel.OnDuty.Value) { holidayDutiesCount++; } } if (holidayDutiesCount >= doctorDutyRequirement.RequiredTotalHolidayDuties) { rows.Add(j); // niedostępny w święta - wyczerpał limit świąt } } else //sprawdzamy dni powszednie { var doctorDuties = dna.GetRow(j); int weekdayDutiesCount = 0; // liczymy ile dyżurów ma w dni powszednie for (int d = 0; d < doctorDuties.Length; d++) { if ((!IsWeekendOrPublicHoliday(new DateTime(_date.Year, _date.Month, d + 1))) && doctorDuties[d] != null && doctorDuties[d].Value == Allel.OnDuty.Value) { weekdayDutiesCount++; } } if (weekdayDutiesCount >= doctorDutyRequirement.RequiredTotalWeekdayDuties) { rows.Add(j); // niedsotępny w dniu powszednie - wyczerpał limit } } } } } } } // tutaj musimy dodać niedostepnych do rows var inaccessibleRows = rows.Union(unavailableDoctors).ToList(); if (onDutyCount < _numberOfDoctorsOnDuty) // jeśli na dyżurze jest mniej niż wymagane uzupełniamy { InsertMissingAllels(dna, i, onDutyCount, inaccessibleRows); } rows.Clear(); } return(dna); }