.NET 4.x Разработка генетического алгоритма составления расписания ВУЗа - C#

Узнай цену своей работы

Формулировка задачи:

Доброго времени суток! Очень нужна помощь в разработке ГА для составления расписания ВУЗа, с учетом множества условий. Никогда до этого с ГА не сталкивался, но прочитав пару статей и посидев на форумах принцип понял, а разработать свой - никак. У меня программа, которая написана на C#. Полностью формирует нагрузку ВУЗа. Учитывая деление групп на подгруппы. Теперь задача состоит в том, чтобы эту нагрузку распределить на неделю, чтобы у студентов было максимально меньше "окон". Можно и другие алгоритмы, которые относятся к оптимизации. Очень буду благодарен.

Решение задачи: «.NET 4.x Разработка генетического алгоритма составления расписания ВУЗа»

textual
Листинг программы
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace LessonPlanNS
  7. {
  8.     static class Program
  9.     {
  10.         static void Main()
  11.         {
  12.             int[] groups   = new int[]{1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5};
  13.             int[] teachers = new int[]{4,1,2,5,3,2,5,4,3,3,1,2,2,4,3,3,3,1,5,1,5,2,2,5,1,4,5,4,1,4};
  14.  
  15.             var list = new List<Lessоn>();
  16.             for (int i = 0; i < groups.Length; i++)
  17.                 list.Add(new Lessоn(groups[i], teachers[i]));
  18.  
  19.             var solver = new Solver();//создаем решатель
  20.  
  21.             Plan.DaysPerWeek = 2;//устанавливаем только два учебных дна - это нужно лишь для данной тестовой задачи, в реальности - выставьте нужное число учебных дней!
  22.             Plan.HoursPerDay = 6;
  23.  
  24.             solver.FitnessFunctions.Add(FitnessFunctions.Windows);//будем штрафовать за окна
  25.             solver.FitnessFunctions.Add(FitnessFunctions.LateLesson);//будем штрафовать за поздние пары
  26.  
  27.             var res = solver.Solve(list);//находим лучший план
  28.  
  29.             Console.WriteLine(res);
  30.             Console.ReadLine();
  31.         }
  32.     }
  33.  
  34.     /// <summary>
  35.     /// Фитнесс функции
  36.     /// </summary>
  37.     static class FitnessFunctions
  38.     {
  39.         public static int GroupWindowPenalty = 10;//штраф за окно у группы
  40.         public static int TeacherWindowPenalty = 7;//штраф за окно у преподавателя
  41.         public static int LateLessonPenalty = 1;//штраф за позднюю пару
  42.  
  43.         public static int LatesetHour = 3;//максимальный час, когда удобно проводить пары
  44.  
  45.         /// <summary>
  46.         /// Штраф за окна
  47.         /// </summary>
  48.         public static int Windows(Plan plan)
  49.         {
  50.             var res = 0;
  51.  
  52.             for (byte day = 0; day < Plan.DaysPerWeek; day++)
  53.             {
  54.                 var groupHasLessions = new HashSet<int>();
  55.                 var teacherHasLessions = new HashSet<int>();
  56.  
  57.                 for (byte hour = 0; hour < Plan.HoursPerDay; hour++)
  58.                 {
  59.                     foreach (var pair in plan.HourPlans[day, hour].GroupToTeacher)
  60.                     {
  61.                         var group = pair.Key;
  62.                         var teacher = pair.Value;
  63.                         if (groupHasLessions.Contains(group) && !plan.HourPlans[day, hour - 1].GroupToTeacher.ContainsKey(group))
  64.                             res += GroupWindowPenalty;
  65.                         if (teacherHasLessions.Contains(teacher) && !plan.HourPlans[day, hour - 1].TeacherToGroup.ContainsKey(teacher))
  66.                             res += TeacherWindowPenalty;
  67.  
  68.                         groupHasLessions.Add(group);
  69.                         teacherHasLessions.Add(teacher);
  70.                     }
  71.                 }
  72.             }
  73.  
  74.             return res;
  75.         }
  76.  
  77.         /// <summary>
  78.         /// Штраф за поздние пары
  79.         /// </summary>
  80.         public static int LateLesson(Plan plan)
  81.         {
  82.             var res = 0;
  83.             foreach (var pair in plan.GetLessons())
  84.                 if (pair.Hour > LatesetHour)
  85.                     res += LateLessonPenalty;
  86.  
  87.             return res;
  88.         }
  89.     }
  90.  
  91.     /// <summary>
  92.     /// Решатель (генетический алгоритм)
  93.     /// </summary>
  94.     class Solver
  95.     {
  96.         public int MaxIterations = 1000;
  97.         public int PopulationCount = 100;//должно делиться на 4
  98.  
  99.         public List<Func<Plan, int>> FitnessFunctions = new List<Func<Plan, int>>();
  100.  
  101.         public int Fitness(Plan plan)
  102.         {
  103.             var res = 0;
  104.  
  105.             foreach (var f in FitnessFunctions)
  106.                 res += f(plan);
  107.  
  108.             return res;
  109.         }
  110.  
  111.         public Plan Solve(List<Lessоn> pairs)
  112.         {
  113.             //создаем популяцию
  114.             var pop = new Population(pairs, PopulationCount);
  115.             if (pop.Count == 0)
  116.                 throw new Exception("Can not create any plan");
  117.             //
  118.             var count = MaxIterations;
  119.             while (count-- > 0)
  120.             {
  121.                 //считаем фитнесс функцию для всех планов
  122.                 pop.ForEach(p => p.FitnessValue = Fitness(p));
  123.                 //сортруем популяцию по фитнесс функции
  124.                 pop.Sort((p1, p2) => p1.FitnessValue.CompareTo(p2.FitnessValue));
  125.                 //найден идеальный план?
  126.                 if (pop[0].FitnessValue == 0)
  127.                     return pop[0];
  128.                 //отбираем 25% лучших планов
  129.                 pop.RemoveRange(pop.Count / 4, pop.Count - pop.Count / 4);
  130.                 //от каждого создаем трех потомков с мутациями
  131.                 var c = pop.Count;
  132.                 for (int i = 0; i < c; i++)
  133.                 {
  134.                     pop.AddChildOfParent(pop[i]);
  135.                     pop.AddChildOfParent(pop[i]);
  136.                     pop.AddChildOfParent(pop[i]);
  137.                 }
  138.             }
  139.  
  140.             //считаем фитнесс функцию для всех планов
  141.             pop.ForEach(p => p.FitnessValue = Fitness(p));
  142.             //сортруем популяцию по фитнесс функции
  143.             pop.Sort((p1, p2) => p1.FitnessValue.CompareTo(p2.FitnessValue));
  144.  
  145.             //возвращаем лучший план
  146.             return pop[0];
  147.         }
  148.     }
  149.  
  150.     /// <summary>
  151.     /// Популяция планов
  152.     /// </summary>
  153.     class Population : List<Plan>
  154.     {
  155.         public Population(List<Lessоn> pairs, int count)
  156.         {
  157.             var maxIterations = count * 2;
  158.  
  159.             do
  160.             {
  161.                 var plan = new Plan();
  162.                 if (plan.Init(pairs))
  163.                     Add(plan);
  164.             } while (maxIterations-- > 0 && Count < count);
  165.         }
  166.  
  167.         public bool AddChildOfParent(Plan parent)
  168.         {
  169.             int maxIterations = 10;
  170.  
  171.             do
  172.             {
  173.                 var plan = new Plan();
  174.                 if (plan.Init(parent))
  175.                 {
  176.                     Add(plan);
  177.                     return true;
  178.                 }
  179.             } while (maxIterations-- > 0);
  180.             return false;
  181.         }
  182.     }
  183.  
  184.     /// <summary>
  185.     /// План занятий
  186.     /// </summary>
  187.     class Plan
  188.     {
  189.         public static int DaysPerWeek = 6;//6 учебных дня в неделю
  190.         public static int HoursPerDay = 6;//до 6 пар в день
  191.  
  192.         static Random rnd = new Random(3);
  193.  
  194.         /// <summary>
  195.         /// План по дням (первый индекс) и часам (второй индекс)
  196.         /// </summary>
  197.         public HourPlan[,] HourPlans = new HourPlan[DaysPerWeek, HoursPerDay];
  198.  
  199.         public int FitnessValue { get; internal set; }
  200.  
  201.         public bool AddLesson(Lessоn les)
  202.         {
  203.             return HourPlans[les.Day, les.Hour].AddLesson(les.Group, les.Teacher);
  204.         }
  205.  
  206.         public void RemoveLesson(Lessоn les)
  207.         {
  208.             HourPlans[les.Day, les.Hour].RemoveLesson(les.Group, les.Teacher);
  209.         }
  210.  
  211.         /// <summary>
  212.         /// Добавить группу с преподом на любой день и любой час
  213.         /// </summary>
  214.         public bool AddToAnyDayAndHour(int group, int teacher)
  215.         {
  216.             int maxIterations = 30;
  217.             do
  218.             {
  219.                 var day = (byte)rnd.Next(DaysPerWeek);
  220.                 if (AddToAnyHour(day, group, teacher))
  221.                     return true;
  222.             } while (maxIterations-- > 0);
  223.  
  224.             return false;//не смогли добавить никуда
  225.         }
  226.  
  227.         /// <summary>
  228.         /// Добавить группу с преподом на любой час
  229.         /// </summary>
  230.         bool AddToAnyHour(byte day, int group, int teacher)
  231.         {
  232.             for (byte hour = 0; hour < HoursPerDay; hour++)
  233.             {
  234.                 var les = new Lessоn(day, hour, group, teacher);
  235.                 if (AddLesson(les))
  236.                     return true;
  237.             }
  238.  
  239.             return false;//нет свободных часов в этот день
  240.         }
  241.  
  242.         /// <summary>
  243.         /// Создание плана по списку пар
  244.         /// </summary>
  245.         public bool Init(List<Lessоn> pairs)
  246.         {
  247.             for (int i = 0; i < HoursPerDay; i++)
  248.                 for (int j = 0; j < DaysPerWeek; j++)
  249.                     HourPlans[j, i] = new HourPlan();
  250.  
  251.             foreach (var p in pairs)
  252.                 if (!AddToAnyDayAndHour(p.Group, p.Teacher))
  253.                     return false;
  254.             return true;
  255.         }
  256.  
  257.         /// <summary>
  258.         /// Создание наследника с мутацией
  259.         /// </summary>
  260.         public bool Init(Plan parent)
  261.         {
  262.             //копируем предка
  263.             for (int i = 0; i < HoursPerDay; i++)
  264.                 for (int j = 0; j < DaysPerWeek; j++)
  265.                     HourPlans[j, i] = parent.HourPlans[j, i].Clone();
  266.  
  267.             //выбираем два случайных дня
  268.             var day1 = (byte)rnd.Next(DaysPerWeek);
  269.             var day2 = (byte)rnd.Next(DaysPerWeek);
  270.  
  271.             //находим пары в эти дни
  272.             var pairs1 = GetLessonsOfDay(day1).ToList();
  273.             var pairs2 = GetLessonsOfDay(day2).ToList();
  274.  
  275.             //выбираем случайные пары
  276.             if (pairs1.Count == 0 || pairs2.Count == 0) return false;
  277.             var pair1 = pairs1[rnd.Next(pairs1.Count)];
  278.             var pair2 = pairs2[rnd.Next(pairs2.Count)];
  279.  
  280.             //создаем мутацию - переставляем случайные пары местами
  281.             RemoveLesson(pair1);//удаляем
  282.             RemoveLesson(pair2);//удаляем
  283.             var res1 = AddToAnyHour(pair2.Day, pair1.Group, pair1.Teacher);//вставляем в случайное место
  284.             var res2 = AddToAnyHour(pair1.Day, pair2.Group, pair2.Teacher);//вставляем в случайное место
  285.             return res1 && res2;
  286.         }
  287.  
  288.         public IEnumerable<Lessоn> GetLessonsOfDay(byte day)
  289.         {
  290.             for (byte hour = 0; hour < HoursPerDay; hour++)
  291.                 foreach (var p in HourPlans[day, hour].GroupToTeacher)
  292.                     yield return new Lessоn(day, hour, p.Key, p.Value);
  293.         }
  294.  
  295.         public IEnumerable<Lessоn> GetLessons()
  296.         {
  297.             for (byte day = 0; day < DaysPerWeek; day++)
  298.                 for (byte hour = 0; hour < HoursPerDay; hour++)
  299.                     foreach (var p in HourPlans[day, hour].GroupToTeacher)
  300.                         yield return new Lessоn(day, hour, p.Key, p.Value);
  301.         }
  302.  
  303.         public override string ToString()
  304.         {
  305.             var sb = new StringBuilder();
  306.             for (byte day = 0; day < Plan.DaysPerWeek; day++)
  307.             {
  308.                 sb.AppendFormat("Day {0}\r\n", day);
  309.                 for (byte hour = 0; hour < Plan.HoursPerDay; hour++)
  310.                 {
  311.                     sb.AppendFormat("Hour {0}: ", hour);
  312.                     foreach (var p in HourPlans[day, hour].GroupToTeacher)
  313.                         sb.AppendFormat("Gr-Tch: {0}-{1} ", p.Key, p.Value);
  314.                     sb.AppendLine();
  315.                 }
  316.             }
  317.  
  318.             sb.AppendFormat("Fitness: {0}\r\n", FitnessValue);
  319.  
  320.             return sb.ToString();
  321.         }
  322.     }
  323.  
  324.     /// <summary>
  325.     /// План на час
  326.     /// </summary>
  327.     class HourPlan
  328.     {
  329.         /// <summary>
  330.         /// Хранит пару группа-преподаватель
  331.         /// </summary>
  332.         public Dictionary<int, int> GroupToTeacher = new Dictionary<int, int>();
  333.  
  334.         /// <summary>
  335.         /// Хранит пару преподаватель-группа
  336.         /// </summary>
  337.         public Dictionary<int, int> TeacherToGroup = new Dictionary<int, int>();
  338.  
  339.         public bool AddLesson(int group, int teacher)
  340.         {
  341.             if (TeacherToGroup.ContainsKey(teacher) || GroupToTeacher.ContainsKey(group))
  342.                 return false;//в этот час уже есть пара у препода или у группы
  343.  
  344.             GroupToTeacher[group] = teacher;
  345.             TeacherToGroup[teacher] = group;
  346.  
  347.             return true;
  348.         }
  349.  
  350.         public void RemoveLesson(int group, int teacher)
  351.         {
  352.             GroupToTeacher.Remove(group);
  353.             TeacherToGroup.Remove(teacher);
  354.         }
  355.  
  356.         public HourPlan Clone()
  357.         {
  358.             var res = new HourPlan();
  359.             res.GroupToTeacher = new Dictionary<int, int>(GroupToTeacher);
  360.             res.TeacherToGroup = new Dictionary<int, int>(TeacherToGroup);
  361.  
  362.             return res;
  363.         }
  364.     }
  365.  
  366.     /// <summary>
  367.     /// Пара
  368.     /// </summary>
  369.     class Lessоn
  370.     {
  371.         public byte Day = 255;
  372.         public byte Hour = 255;
  373.         public int Group;
  374.         public int Teacher;
  375.  
  376.         public Lessоn(byte day, byte hour, int group, int teacher)
  377.             : this(group, teacher)
  378.         {
  379.             Day = day;
  380.             Hour = hour;
  381.         }
  382.  
  383.         public Lessоn(int group, int teacher)
  384.         {
  385.             Group = group;
  386.             Teacher = teacher;
  387.         }
  388.     }
  389. }

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

14   голосов , оценка 3.786 из 5

Нужна аналогичная работа?

Оформи быстрый заказ и узнай стоимость

Бесплатно
Оформите заказ и авторы начнут откликаться уже через 10 минут
Похожие ответы