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

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

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

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

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

textual
Листинг программы
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace LessonPlanNS
{
    static class Program
    {
        static void Main()
        {
            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};
            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};
 
            var list = new List<Lessоn>();
            for (int i = 0; i < groups.Length; i++)
                list.Add(new Lessоn(groups[i], teachers[i]));
 
            var solver = new Solver();//создаем решатель
 
            Plan.DaysPerWeek = 2;//устанавливаем только два учебных дна - это нужно лишь для данной тестовой задачи, в реальности - выставьте нужное число учебных дней!
            Plan.HoursPerDay = 6;
 
            solver.FitnessFunctions.Add(FitnessFunctions.Windows);//будем штрафовать за окна
            solver.FitnessFunctions.Add(FitnessFunctions.LateLesson);//будем штрафовать за поздние пары
 
            var res = solver.Solve(list);//находим лучший план
 
            Console.WriteLine(res);
            Console.ReadLine();
        }
    }
 
    /// <summary>
    /// Фитнесс функции
    /// </summary>
    static class FitnessFunctions
    {
        public static int GroupWindowPenalty = 10;//штраф за окно у группы
        public static int TeacherWindowPenalty = 7;//штраф за окно у преподавателя
        public static int LateLessonPenalty = 1;//штраф за позднюю пару
 
        public static int LatesetHour = 3;//максимальный час, когда удобно проводить пары
 
        /// <summary>
        /// Штраф за окна
        /// </summary>
        public static int Windows(Plan plan)
        {
            var res = 0;
 
            for (byte day = 0; day < Plan.DaysPerWeek; day++)
            {
                var groupHasLessions = new HashSet<int>();
                var teacherHasLessions = new HashSet<int>();
 
                for (byte hour = 0; hour < Plan.HoursPerDay; hour++)
                {
                    foreach (var pair in plan.HourPlans[day, hour].GroupToTeacher)
                    {
                        var group = pair.Key;
                        var teacher = pair.Value;
                        if (groupHasLessions.Contains(group) && !plan.HourPlans[day, hour - 1].GroupToTeacher.ContainsKey(group))
                            res += GroupWindowPenalty;
                        if (teacherHasLessions.Contains(teacher) && !plan.HourPlans[day, hour - 1].TeacherToGroup.ContainsKey(teacher))
                            res += TeacherWindowPenalty;
 
                        groupHasLessions.Add(group);
                        teacherHasLessions.Add(teacher);
                    }
                }
            }
 
            return res;
        }
 
        /// <summary>
        /// Штраф за поздние пары
        /// </summary>
        public static int LateLesson(Plan plan)
        {
            var res = 0;
            foreach (var pair in plan.GetLessons())
                if (pair.Hour > LatesetHour)
                    res += LateLessonPenalty;
 
            return res;
        }
    }
 
    /// <summary>
    /// Решатель (генетический алгоритм)
    /// </summary>
    class Solver
    {
        public int MaxIterations = 1000;
        public int PopulationCount = 100;//должно делиться на 4
 
        public List<Func<Plan, int>> FitnessFunctions = new List<Func<Plan, int>>();
 
        public int Fitness(Plan plan)
        {
            var res = 0;
 
            foreach (var f in FitnessFunctions)
                res += f(plan);
 
            return res;
        }
 
        public Plan Solve(List<Lessоn> pairs)
        {
            //создаем популяцию
            var pop = new Population(pairs, PopulationCount);
            if (pop.Count == 0)
                throw new Exception("Can not create any plan");
            //
            var count = MaxIterations;
            while (count-- > 0)
            {
                //считаем фитнесс функцию для всех планов
                pop.ForEach(p => p.FitnessValue = Fitness(p));
                //сортруем популяцию по фитнесс функции
                pop.Sort((p1, p2) => p1.FitnessValue.CompareTo(p2.FitnessValue));
                //найден идеальный план?
                if (pop[0].FitnessValue == 0)
                    return pop[0];
                //отбираем 25% лучших планов
                pop.RemoveRange(pop.Count / 4, pop.Count - pop.Count / 4);
                //от каждого создаем трех потомков с мутациями
                var c = pop.Count;
                for (int i = 0; i < c; i++)
                {
                    pop.AddChildOfParent(pop[i]);
                    pop.AddChildOfParent(pop[i]);
                    pop.AddChildOfParent(pop[i]);
                }
            }
 
            //считаем фитнесс функцию для всех планов
            pop.ForEach(p => p.FitnessValue = Fitness(p));
            //сортруем популяцию по фитнесс функции
            pop.Sort((p1, p2) => p1.FitnessValue.CompareTo(p2.FitnessValue));
 
            //возвращаем лучший план
            return pop[0];
        }
    }
 
    /// <summary>
    /// Популяция планов
    /// </summary>
    class Population : List<Plan>
    {
        public Population(List<Lessоn> pairs, int count)
        {
            var maxIterations = count * 2;
 
            do
            {
                var plan = new Plan();
                if (plan.Init(pairs))
                    Add(plan);
            } while (maxIterations-- > 0 && Count < count);
        }
 
        public bool AddChildOfParent(Plan parent)
        {
            int maxIterations = 10;
 
            do
            {
                var plan = new Plan();
                if (plan.Init(parent))
                {
                    Add(plan);
                    return true;
                }
            } while (maxIterations-- > 0);
            return false;
        }
    }
 
    /// <summary>
    /// План занятий
    /// </summary>
    class Plan
    {
        public static int DaysPerWeek = 6;//6 учебных дня в неделю
        public static int HoursPerDay = 6;//до 6 пар в день
 
        static Random rnd = new Random(3);
 
        /// <summary>
        /// План по дням (первый индекс) и часам (второй индекс)
        /// </summary>
        public HourPlan[,] HourPlans = new HourPlan[DaysPerWeek, HoursPerDay];
 
        public int FitnessValue { get; internal set; }
 
        public bool AddLesson(Lessоn les)
        {
            return HourPlans[les.Day, les.Hour].AddLesson(les.Group, les.Teacher);
        }
 
        public void RemoveLesson(Lessоn les)
        {
            HourPlans[les.Day, les.Hour].RemoveLesson(les.Group, les.Teacher);
        }
 
        /// <summary>
        /// Добавить группу с преподом на любой день и любой час
        /// </summary>
        public bool AddToAnyDayAndHour(int group, int teacher)
        {
            int maxIterations = 30;
            do
            {
                var day = (byte)rnd.Next(DaysPerWeek);
                if (AddToAnyHour(day, group, teacher))
                    return true;
            } while (maxIterations-- > 0);
 
            return false;//не смогли добавить никуда
        }
 
        /// <summary>
        /// Добавить группу с преподом на любой час
        /// </summary>
        bool AddToAnyHour(byte day, int group, int teacher)
        {
            for (byte hour = 0; hour < HoursPerDay; hour++)
            {
                var les = new Lessоn(day, hour, group, teacher);
                if (AddLesson(les))
                    return true;
            }
 
            return false;//нет свободных часов в этот день
        }
 
        /// <summary>
        /// Создание плана по списку пар
        /// </summary>
        public bool Init(List<Lessоn> pairs)
        {
            for (int i = 0; i < HoursPerDay; i++)
                for (int j = 0; j < DaysPerWeek; j++)
                    HourPlans[j, i] = new HourPlan();
 
            foreach (var p in pairs)
                if (!AddToAnyDayAndHour(p.Group, p.Teacher))
                    return false;
            return true;
        }
 
        /// <summary>
        /// Создание наследника с мутацией
        /// </summary>
        public bool Init(Plan parent)
        {
            //копируем предка
            for (int i = 0; i < HoursPerDay; i++)
                for (int j = 0; j < DaysPerWeek; j++)
                    HourPlans[j, i] = parent.HourPlans[j, i].Clone();
 
            //выбираем два случайных дня
            var day1 = (byte)rnd.Next(DaysPerWeek);
            var day2 = (byte)rnd.Next(DaysPerWeek);
 
            //находим пары в эти дни
            var pairs1 = GetLessonsOfDay(day1).ToList();
            var pairs2 = GetLessonsOfDay(day2).ToList();
 
            //выбираем случайные пары
            if (pairs1.Count == 0 || pairs2.Count == 0) return false;
            var pair1 = pairs1[rnd.Next(pairs1.Count)];
            var pair2 = pairs2[rnd.Next(pairs2.Count)];
 
            //создаем мутацию - переставляем случайные пары местами
            RemoveLesson(pair1);//удаляем
            RemoveLesson(pair2);//удаляем
            var res1 = AddToAnyHour(pair2.Day, pair1.Group, pair1.Teacher);//вставляем в случайное место
            var res2 = AddToAnyHour(pair1.Day, pair2.Group, pair2.Teacher);//вставляем в случайное место
            return res1 && res2;
        }
 
        public IEnumerable<Lessоn> GetLessonsOfDay(byte day)
        {
            for (byte hour = 0; hour < HoursPerDay; hour++)
                foreach (var p in HourPlans[day, hour].GroupToTeacher)
                    yield return new Lessоn(day, hour, p.Key, p.Value);
        }
 
        public IEnumerable<Lessоn> GetLessons()
        {
            for (byte day = 0; day < DaysPerWeek; day++)
                for (byte hour = 0; hour < HoursPerDay; hour++)
                    foreach (var p in HourPlans[day, hour].GroupToTeacher)
                        yield return new Lessоn(day, hour, p.Key, p.Value);
        }
 
        public override string ToString()
        {
            var sb = new StringBuilder();
            for (byte day = 0; day < Plan.DaysPerWeek; day++)
            {
                sb.AppendFormat("Day {0}\r\n", day);
                for (byte hour = 0; hour < Plan.HoursPerDay; hour++)
                {
                    sb.AppendFormat("Hour {0}: ", hour);
                    foreach (var p in HourPlans[day, hour].GroupToTeacher)
                        sb.AppendFormat("Gr-Tch: {0}-{1} ", p.Key, p.Value);
                    sb.AppendLine();
                }
            }
 
            sb.AppendFormat("Fitness: {0}\r\n", FitnessValue);
 
            return sb.ToString();
        }
    }
 
    /// <summary>
    /// План на час
    /// </summary>
    class HourPlan
    {
        /// <summary>
        /// Хранит пару группа-преподаватель
        /// </summary>
        public Dictionary<int, int> GroupToTeacher = new Dictionary<int, int>();
 
        /// <summary>
        /// Хранит пару преподаватель-группа
        /// </summary>
        public Dictionary<int, int> TeacherToGroup = new Dictionary<int, int>();
 
        public bool AddLesson(int group, int teacher)
        {
            if (TeacherToGroup.ContainsKey(teacher) || GroupToTeacher.ContainsKey(group))
                return false;//в этот час уже есть пара у препода или у группы
 
            GroupToTeacher[group] = teacher;
            TeacherToGroup[teacher] = group;
 
            return true;
        }
 
        public void RemoveLesson(int group, int teacher)
        {
            GroupToTeacher.Remove(group);
            TeacherToGroup.Remove(teacher);
        }
 
        public HourPlan Clone()
        {
            var res = new HourPlan();
            res.GroupToTeacher = new Dictionary<int, int>(GroupToTeacher);
            res.TeacherToGroup = new Dictionary<int, int>(TeacherToGroup);
 
            return res;
        }
    }
 
    /// <summary>
    /// Пара
    /// </summary>
    class Lessоn
    {
        public byte Day = 255;
        public byte Hour = 255;
        public int Group;
        public int Teacher;
 
        public Lessоn(byte day, byte hour, int group, int teacher)
            : this(group, teacher)
        {
            Day = day;
            Hour = hour;
        }
 
        public Lessоn(int group, int teacher)
        {
            Group = group;
            Teacher = teacher;
        }
    }
}

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


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

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

14   голосов , оценка 3.786 из 5
Похожие ответы