旅行推销员问题使用遗传算法

huangapple go评论103阅读模式
英文:

The Travelling Salesman Problem Using Genetic Algorithm

问题

我尝试调整参数值,但最终距离始终在上限9500左右,从未降至9000以下。我该如何改进以使其适用于我的位置文件?

  1. pop_size = 100 # Increase population size
  2. elite_size = 40 # Try adjusting the elite size
  3. mutation_rate = 0.001 # Experiment with different mutation rates
  4. gen = 500 # You can increase the number of generations
  5. # Consider adjusting the following parameters based on the characteristics of your problem and results:
  6. # - ordered_crossover
  7. # - fit_proportionate_selection
  8. # Example:
  9. # def ordered_crossover_pop(mating_pool, elite_size):
  10. # ...
  11. # def fit_proportionate_selection(top_pop, elite_size):
  12. # ...

请尝试更改这些参数,以及调整遗传算法的其他相关函数,以找到更好的解决方案。

英文:

I was looking to learn about AI and found the traveling salesman problem very interesting. I also wanted to learn about genetic algorithms, so it was a fantastic combo. The task is to find the shortest distance traveling from id 1 to each location from the list once and returning to the starting location id 1

Restriction for the problem :

> The location id 1 must be the starting and the ending point
>
> The maximum distance allowed is distance <= 9000
>
> Only max of 250000 fitness calculation is allowed


Code :

  1. import numpy as np
  2. import random
  3. import operator
  4. import pandas as pd
  5. val10 = 0
  6. val9 = 0
  7. class Locations:
  8. def __init__(self, x, y):
  9. self.x = x
  10. self.y = y
  11. def dist(self, location):
  12. x_dist = abs(float(self.x) - float(location.x))
  13. y_dist = abs(float(self.y) - float(location.y))
  14. # √( (x2 − x1)^2 + (𝑦2 − 𝑦1)^2 )
  15. dist = np.sqrt((x_dist ** 2) + (y_dist ** 2))
  16. return dist
  17. def __repr__(self):
  18. return "(" + str(self.x) + "," + str(self.y) + ")"
  19. class Fitness:
  20. def __init__(self, route):
  21. self.r = route
  22. self.dist = 0
  23. self.fit = 0.0
  24. def route_dist(self):
  25. if self.dist == 0:
  26. path_dist = 0
  27. for i in range(0, len(self.r)):
  28. from_location = self.r[i]
  29. to_location = None
  30. if i + 1 < len(self.r):
  31. to_location = self.r[i+1]
  32. else:
  33. to_location = self.r[0]
  34. path_dist += from_location.dist(to_location)
  35. self.dist = path_dist
  36. return self.dist
  37. def route_fittness(self):
  38. if self.fit == 0:
  39. self.fit = 1 / float(self.route_dist())
  40. global val9
  41. val9 = val9 + 1
  42. return self.fit
  43. def generate_route(location_list):
  44. route = random.sample(location_list, len(location_list))
  45. return route
  46. def gen_zero_population(size, location_list):
  47. population = []
  48. for i in range(0, size):
  49. population.append(generate_route(location_list))
  50. return population
  51. def determine_fit(population):
  52. result = {}
  53. for i in range(0, len(population)):
  54. result[i] = Fitness(population[i]).route_fittness()
  55. global val10
  56. val10 = val10 + 1
  57. return sorted(result.items(), key=operator.itemgetter(1), reverse=True)
  58. def fit_proportionate_selection(top_pop, elite_size):
  59. result = []
  60. df = pd.DataFrame(np.array(top_pop), columns=["index", "Fitness"])
  61. df['cumulative_sum'] = df.Fitness.cumsum()
  62. df['Sum'] = 100*df.cumulative_sum/df.Fitness.sum()
  63. for i in range(0, elite_size):
  64. result.append(top_pop[i][0])
  65. for i in range(0, len(top_pop) - elite_size):
  66. select = 100*random.random()
  67. for i in range(0, len(top_pop)):
  68. if select <= df.iat[i, 3]:
  69. result.append(top_pop[i][0])
  70. break
  71. return result
  72. def select_mating_pool(populatoin, f_p_s_result):
  73. mating_pool = []
  74. for i in range(0, len(f_p_s_result)):
  75. index = f_p_s_result[i]
  76. mating_pool.append(populatoin[index])
  77. return mating_pool
  78. def ordered_crossover(p1, p2):
  79. child, child_p1, child_p2 = ([] for i in range(3))
  80. first_gene = int(random.random() * len(p1))
  81. sec_gene = int(random.random() * len(p2))
  82. start_gene = min(first_gene, sec_gene)
  83. end_gene = max(first_gene, sec_gene)
  84. for i in range(start_gene, end_gene):
  85. child_p1.append(p1[i])
  86. child_p2 = [item for item in p2 if item not in child_p1]
  87. child = child_p1 + child_p2
  88. return child
  89. def ordered_crossover_pop(mating_pool, elite_size):
  90. children = []
  91. leng = (len(mating_pool) - (elite_size))
  92. pool = random.sample(mating_pool, len(mating_pool))
  93. for i in range(0, elite_size):
  94. children.append(mating_pool[i])
  95. for i in range(0, leng):
  96. var = len(mating_pool)-i - 1
  97. child = ordered_crossover(pool[i], pool[var])
  98. children.append(child)
  99. return children
  100. def swap_mutation(one_location, mutation_rate):
  101. for i in range(len(one_location)):
  102. if (random.random() < mutation_rate):
  103. swap = int(random.random() * len(one_location))
  104. location1 = one_location[i]
  105. location2 = one_location[swap]
  106. one_location[i] = location2
  107. one_location[swap] = location1
  108. return one_location
  109. def pop_mutation(population, mutation_rate):
  110. result = []
  111. for i in range(0, len(population)):
  112. mutaded_res = swap_mutation(population[i], mutation_rate)
  113. result.append(mutaded_res)
  114. return result
  115. def next_gen(latest_gen, elite_size, mutation_rate):
  116. route_rank = determine_fit(latest_gen)
  117. selection = fit_proportionate_selection(route_rank, elite_size)
  118. mating_selection = select_mating_pool(latest_gen, selection)
  119. children = ordered_crossover_pop(mating_selection, elite_size)
  120. next_generation = pop_mutation(children, mutation_rate)
  121. return next_generation
  122. def generic_algor(population, pop_size, elite_size, mutation_rate, gen):
  123. pop = gen_zero_population(pop_size, population)
  124. print("Initial distance: " + str(1 / determine_fit(pop)[0][1]))
  125. for i in range(0, gen):
  126. pop = next_gen(pop, elite_size, mutation_rate)
  127. print("Final distance: " + str(1 / determine_fit(pop)[0][1]))
  128. best_route_index = determine_fit(pop)[0][0]
  129. best_route = pop[best_route_index]
  130. print(best_route)
  131. return best_route
  132. def read_file(fn):
  133. a = []
  134. with open(fn) as f:
  135. [next(f) for _ in range(6)]
  136. for line in f:
  137. line = line.rstrip()
  138. if line == 'EOF':
  139. break
  140. ID, x, y = line.split()
  141. a.append(Locations(x=x, y=y))
  142. return a
  143. location_list = read_file(r'path_of_the_file')
  144. population = location_list
  145. pop_size = 100
  146. elite_size = 40
  147. mutation_rate = 0.001
  148. gen = 500
  149. generic_algor(population, pop_size, elite_size, mutation_rate, gen)
  150. print(val10)
  151. print(val9)

Location file with x and y coordinates :

  1. |Locations
  2. |
  3. |52 Locations
  4. |
  5. |Coordinates
  6. |
  7. 1 565.0 575.0
  8. 2 25.0 185.0
  9. 3 345.0 750.0
  10. 4 945.0 685.0
  11. 5 845.0 655.0
  12. 6 880.0 660.0
  13. 7 25.0 230.0
  14. 8 525.0 1000.0
  15. 9 580.0 1175.0
  16. 10 650.0 1130.0
  17. 11 1605.0 620.0
  18. 12 1220.0 580.0
  19. 13 1465.0 200.0
  20. 14 1530.0 5.0
  21. 15 845.0 680.0
  22. 16 725.0 370.0
  23. 17 145.0 665.0
  24. 18 415.0 635.0
  25. 19 510.0 875.0
  26. 20 560.0 365.0
  27. 21 300.0 465.0
  28. 22 520.0 585.0
  29. 23 480.0 415.0
  30. 24 835.0 625.0
  31. 25 975.0 580.0
  32. 26 1215.0 245.0
  33. 27 1320.0 315.0
  34. 28 1250.0 400.0
  35. 29 660.0 180.0
  36. 30 410.0 250.0
  37. 31 420.0 555.0
  38. 32 575.0 665.0
  39. 33 1150.0 1160.0
  40. 34 700.0 580.0
  41. 35 685.0 595.0
  42. 36 685.0 610.0
  43. 37 770.0 610.0
  44. 38 795.0 645.0
  45. 39 720.0 635.0
  46. 40 760.0 650.0
  47. 41 475.0 960.0
  48. 42 95.0 260.0
  49. 43 875.0 920.0
  50. 44 700.0 500.0
  51. 45 555.0 815.0
  52. 46 830.0 485.0
  53. 47 1170.0 65.0
  54. 48 830.0 610.0
  55. 49 605.0 625.0
  56. 50 595.0 360.0
  57. 51 1340.0 725.0
  58. 52 1740.0 245.0
  59. EOF

I have tried to tweak the value of the parameters but it has never gone below or be 9000 it is always around the upper 9500 What can I improve to get it to work for my location file?

旅行推销员问题使用遗传算法

答案1

得分: 0

原来一切都很好,微调distance函数和fit_proportionate_selection确保更快的运行时间,从而使测试参数更快。另一个变化是为random()使用固定种子,这样可以在没有太多变量的情况下进行比较。

  1. def fit_proportionate_selection(top_pop, elite_size):
  2. indices, fitness = zip(*top_pop)
  3. cumulative_sum = list(it.accumulate(fitness))
  4. total = cumulative_sum[-1]
  5. weights = [100*cs/total for cs in cumulative_sum]
  6. result = list(indices[:elite_size])
  7. for i in range(len(top_pop) - elite_size):
  8. select = random.randrange(100)
  9. for i, weight in enumerate(weights):
  10. if select <= weight:
  11. result.append(top_pop[i][0])
  12. break
  13. return result

从我的代码审查问题中提取的参数:

  1. pop_size = 150, elite_size=50, mutation_rate=0.0005, gen=400
英文:

Turns out everything is fine tweaking the distance function and the fit_proportionate_selection ensures a faster run time which makes testing the parameters faster. The other change is to have a fixed seed for the random() this way the results can be compared without much variant.

  1. def fit_proportionate_selection(top_pop, elite_size):
  2. indices, fitness = zip(*top_pop)
  3. cumulative_sum = list(it.accumulate(fitness))
  4. total = cumulative_sum[-1]
  5. weights = [100*cs/total for cs in cumulative_sum]
  6. result = list(indices[:elite_size])
  7. for i in range(len(top_pop) - elite_size):
  8. select = random.randrange(100)
  9. for i, weight in enumerate(weights):
  10. if select &lt;= weight:
  11. result.append(top_pop[i][0])
  12. break
  13. return result

Taken from my review question Code review of the same code

The parameters picked where from the comment of the review question:

  1. pop_size = 150, elite_size=50, mutation_rate=0.0005, gen=400

huangapple
  • 本文由 发表于 2023年4月20日 03:45:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76058279.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定