Задача о ранце. Генетический алгоритм - C#

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

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

Доброго времени суток, коллеги. На днях выдалось относительно свободное время и я решил размять мозги. Выбор пал на задачу о ранце. Начать решил с поиска готовых решений. В нете нашёл простую реализацию решения методом перебора. Небольшой список предметов она решала относительно быстро, при увеличении время работы нелинейно возрастало: на 5 предметов решение находилось за 10 мс, 10 предметов - 4,5 секунды, 15 - задача крутилась 8 часов, после чего мне это надоело и я её вырубил. Вооружившись википедией и тематическими статьями решил реализовать решение генетическим алгоритмом. Вроде сделал, вроде работает, но что-то мне оно не особо нравится: 1) Пришлось хардкодить создание начальной популяции отбрасывая особи, у которых суммарный вес предметов выше максимально допустимого. Знаю, что это допускается условием задачи, но, ИМХО, по идее алгоритм должен их принимать, как и других, просто отсеивая. А по факту в итоге зачастую выживали именно с этим генотипом. 2) Гвоздями прибито условие, по которому популяция должна делиться надвое без остатка. 3) Иногда выживают особи с не самым оптимальным набором предметов, или даже имеющие кучу свободного места. Если подскажете, как можно довести до ума алгоритм буду благодарен. Модель данных:
Листинг программы
  1. public class Item
  2. {
  3. public String Name { get; private set; }
  4. public int Weight { get; private set; }
  5. public double Price { get; private set; }
  6. public Item (String Name, int Weight, double Price)
  7. {
  8. this.Name = Name;
  9. this.Weight = Weight;
  10. this.Price = Price;
  11. }
  12. } public class BackPack
  13. {
  14. List<Item> Items = null;
  15. public int Weight { get; private set; } = 0;
  16. public double Price { get; private set; }
  17. public double Fitness { get; private set; }
  18. public double Probably { get; set; }
  19. public BackPack(int maxWeight, Item[] allItems, Random rnd)
  20. {
  21. int i;
  22. int iterations = 0;
  23. Items = new List<Item>();
  24. while (Weight < maxWeight)
  25. {
  26. iterations++;
  27. if (iterations > 100)
  28. break;
  29. i = rnd.Next(0, allItems.Length);
  30. if (Items.Contains(allItems[i]))
  31. continue;
  32. if (Weight + allItems[i].Weight > maxWeight)
  33. continue;
  34. Items.Add(allItems[i]);
  35. Weight += Items.Last().Weight;
  36. }
  37. Price = Items.Sum(x => x.Price);
  38. Fitness = Math.Round(Price / Weight,3);
  39. }
  40. public Item[] getItems()
  41. {
  42. return Items.ToArray();
  43. }
  44. }
Алгоритм:
Листинг программы
  1. public partial class MainWindow : Window
  2. {
  3. Random rnd = new Random();
  4. List<Item> BaseItems = new List<Item>();
  5. public List<BackPack> population;
  6. const int generationsCount = 1500;
  7. const int populationCount = 60;
  8. const int maxWeight = 15;
  9. public MainWindow()
  10. {
  11. InitializeComponent();
  12. }
  13. private void Button_Click(object sender, RoutedEventArgs e)
  14. {
  15. InitItems();
  16. CreatePopulation();
  17. for (int i = 0; i < generationsCount; i++)
  18. {
  19. Selection();
  20. Mutation();
  21. }
  22. }
  23. /// <summary>
  24. /// Создаём набор предметов
  25. /// </summary>
  26. void InitItems()
  27. {
  28. BaseItems.Add(new Item("Книга", 1, 600));
  29. BaseItems.Add(new Item("Бинокль", 2, 5000));
  30. BaseItems.Add(new Item("Аптечка", 4, 1500));
  31. BaseItems.Add(new Item("Ноутбук", 2, 40000));
  32. BaseItems.Add(new Item("Котелок", 1, 500));
  33. BaseItems.Add(new Item("Книга1", 3, 500));
  34. BaseItems.Add(new Item("Бинокль1", 2, 7000));
  35. BaseItems.Add(new Item("Аптечка1", 5, 500));
  36. BaseItems.Add(new Item("Ноутбук1", 6, 60000));
  37. BaseItems.Add(new Item("Котелок1", 2, 300));
  38. BaseItems.Add(new Item("Книга2", 6, 650));
  39. BaseItems.Add(new Item("Бинокль2", 3, 15000));
  40. BaseItems.Add(new Item("Аптечка2", 1, 15000));
  41. BaseItems.Add(new Item("Ноутбук2", 9, 440000));
  42. BaseItems.Add(new Item("Котелок2", 2, 600));
  43. BaseItems.Add(new Item("Книга3", 6, 200));
  44. BaseItems.Add(new Item("Бинокль3", 4, 500));
  45. BaseItems.Add(new Item("Аптечка3", 4, 150));
  46. BaseItems.Add(new Item("Ноутбук3", 7, 45000));
  47. BaseItems.Add(new Item("Котелок3", 1, 500));
  48. BaseItems.Add(new Item("Книга4", 1, 400));
  49. BaseItems.Add(new Item("Бинокль4", 2, 5500));
  50. BaseItems.Add(new Item("Аптечка4", 2, 2000));
  51. BaseItems.Add(new Item("Ноутбук4", 2, 32000));
  52. BaseItems.Add(new Item("Котелок4", 2, 520));
  53. BaseItems.Add(new Item("Книга5", 7, 640));
  54. BaseItems.Add(new Item("Бинокль5", 6, 5400));
  55. BaseItems.Add(new Item("Аптечка5", 3, 1600));
  56. BaseItems.Add(new Item("Ноутбук5", 2, 21000));
  57. BaseItems.Add(new Item("Котелок5", 2, 500));
  58. }
  59. /// <summary>
  60. /// Создаём начальную популяцию
  61. /// </summary>
  62. void CreatePopulation()
  63. {
  64. population = new List<BackPack>();
  65. for (int i = 0; i < populationCount; i++)
  66. population.Add(new BackPack(maxWeight, BaseItems.ToArray(), rnd));
  67. }
  68. /// <summary>
  69. /// Выполняем селекцию
  70. /// </summary>
  71. void Selection()
  72. {
  73. double SumFitness = population.Sum(x => x.Fitness);
  74. foreach (BackPack bp in population) // для каждого набора расчитываем вероятность быть избранным
  75. bp.Probably = 100 * bp.Fitness / SumFitness;
  76. population = population.OrderByDescending(x => x.Probably).ToList();
  77. List<BackPack> parents = new List<BackPack>();
  78. while (parents.Count != populationCount / 2) // перебирая сортированный список наборов отбираем половину на роль родителей
  79. {
  80. for (int i = 0; i < population.Count; i++)
  81. {
  82. if (population[i].Weight > maxWeight) // отбрасываем нежиснеспособных особей
  83. continue;
  84. if ((rnd.Next(0, 100 + (int)population[i].Probably * 10)) < 50)
  85. continue;
  86. else
  87. {
  88. parents.Add(population[i]);
  89. population.RemoveAt(i);
  90. break;
  91. }
  92. }
  93. }
  94. population = new List<BackPack>();
  95. population.AddRange(parents);
  96. BackPack dad, mum, son1, son2;
  97. List<Item> items;
  98. while(parents.Count > 0) // перебирая родителей отбираем случайным образом пары и создаём 2-х потомков
  99. {
  100. dad = parents[rnd.Next(parents.Count)];
  101. parents.Remove(dad);
  102. mum = parents[rnd.Next(parents.Count)];
  103. parents.Remove(mum);
  104. items = new List<Item>();
  105. items.AddRange(dad.getItems());
  106. items.AddRange(mum.getItems());
  107. son1 = new BackPack(maxWeight, items.ToArray(), rnd);
  108. son1.Probably = 100 * son1.Fitness / SumFitness;
  109. son2 = new BackPack(maxWeight, items.ToArray(), rnd);
  110. son2.Probably = 100 * son2.Fitness / SumFitness;
  111. population.Add(son1);
  112. population.Add(son2);
  113. }
  114. }
  115. /// <summary>
  116. /// Случайная мутация
  117. /// </summary>
  118. void Mutation()
  119. {
  120. BackPack rBp = population[rnd.Next(0, population.Count)];
  121. int index = rnd.Next(0, rBp.getItems().Length);
  122. Item newItem = BaseItems[rnd.Next(0, BaseItems.Count)];
  123. rBp.getItems()[index] = newItem;
  124. }
  125. }
Исходники прилагаются (простое WPF приложение с одной кнопкой) BP_Problem.zip

Решение задачи: «Задача о ранце. Генетический алгоритм»

textual
Листинг программы
  1.     public struct Backpack
  2.     {
  3.         public Backpack(ImmutableArray<Item> items)
  4.         {
  5.             Items = items;
  6.         }
  7.  
  8.         public ImmutableArray<Item> Items { get; }
  9.         public int TotalWorth => Items.Sum(x => x.Worth);
  10.     }

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


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

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

8   голосов , оценка 4.25 из 5

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

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

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