Genetic algorithm to solve tsp problem (based on python)

Table of contents

1. Brief introduction of genetic algorithm

2. A brief introduction to the tsp problem

3. Several special points of genetic algorithm to solve tsp problem

4. Source code

1. Brief introduction of genetic algorithm

        Simply put, a genetic algorithm is a search algorithm used to solve optimization problems. Its core is based on the law of natural population evolution, that is, the initial population is mated, and at the genetic level, there will be crossover, gene mutation and other mutations, resulting in a new batch of populations. In the process of continuous reproduction of the population, the "fittest" Survive, and the unfit perish", the genes of individuals who are more in line with the requirements of the environment are more likely to survive, and the genes of individuals who are not adapted to the environment often do not continue. After a long period of time, a group of populations that are most suitable for the environment will be selected.

        Based on this, when we solve the optimization problem, we adopt the above ideas and regard the solutions of the problem as "individuals". These individuals form an abstract "population", and these solutions are mapped into corresponding codes, so we can A "population" consisting of various codes is obtained. These codes can carry out the cross-exchange of fragments, or some of the numbers have "genetic mutations", so as to update the population. So how to screen more suitable individuals? According to actual needs and constraints, we calculate the "fitness" of each individual through a specific function based on the code, and the genes (code) of individuals with greater fitness have a greater probability of being selected and retained. In this way, after hundreds of iterations, a population close to the optimal solution can be obtained.

        For a detailed introduction, you can refer to this article Genetic Algorithm Detailed Explanation Attached to Python Code Implementation_Re-learning CS Blog-CSDN Blog_python Genetic Algorithm  , the author of the article explained the optimization of general formulas to outrageously detailed and easy-to-understand. If you can master this article, you can basically use the genetic algorithm to find the maximum value of any formula (n-element n-degree equation). The connotation is to map the solution from a decimal number to a binary string, and the encoding and decoding process is very important.

        I won’t go into more details here. The above article is very clear. The focus of this article is to solve the tsp problem.

2. A brief introduction to the tsp problem

        According to the introduction of Baidu Encyclopedia, the t traveling salesman problem, that is, the TSP problem (Traveling Salesman Problem), also translated as the traveling salesman problem and the salesman problem , is one of the famous problems in the field of mathematics . Assuming that a traveling businessman wants to visit n cities, he must choose the path he wants to take. The limit of the path is that each city can only be visited once, and in the end he must return to the original starting city. The goal of path selection is that the required path distance is the minimum value among all paths.

3. Several special points of genetic algorithm to solve tsp problem

This is also the focus of this article

3.1 How to code

First of all, we need to understand what the form of our solution is, we need to get a path with the shortest distance. Therefore, these cities are coded (0, 1, 2......n). Taking 10 cities as an example, the solution we hope to get may be 3 5 4 8 6 7 9 0 2 1. Therefore, in In the genetic algorithm, the form of each individual should be an arrangement of 10 non-repeating numbers. The good news is that this eliminates the need for binary encoding and decoding.

 #初始化种群
def generate_population(self):
    path=np.array(range(self.num))
    self.pop=[]
    for i in range(self.size_pop):
        x=path.copy()
        np.random.shuffle(x)    #随机洗牌
        self.pop.append(x)

3.2 Establishment of distance matrix

How do we evaluate the fitness of a solution? Obviously we want the path distance of each individual to be as small as possible, so we need to get the distance between each city first and record it in a matrix. When we need to find the distance of a complete path later, the distance between any two points Distances can be converted directly to coordinates (say, (2,6)) taken from this matrix.

3.3 Cross swap

It is obviously unreasonable to directly determine a position on a chromosome and directly cross-exchange the father and mother from this position. If the father is 1 3 2 4 and the mother is 2 4 1 3, the two The offspring after crossing and exchanging exactly at the midpoint are 1 3 1 3 and 2 4 2 4, which is obviously wrong! The traveling salesman can only pass through each city once! Therefore, when exchanging chromosome segments, an operation must be performed, which is to remove repeated base pairs.

Detailed interpretation of genetic algorithms for TSP and MTSP problems and python implementation . The blogger of this article gives three ways of cross-swapping genetic algorithms for tsp problems. Here we choose the second one

Order Crossover

​ 

3.4 Gene mutation

In the case of binary coding, in order to complete the gene mutation, it is only necessary to randomly replace one base pair of the selected chromosome. But in the tsp problem, if you do this, it will definitely lead to duplication of the base pairs of the selected chromosomes! So what we need to do is to exchange any two base pairs of the selected chromosomes, thus avoiding duplication

3.5 Fitness Calculation

How do we evaluate whether an individual's genes are suitable for being inherited? This requires calculating the fitness of the individual, the higher the fitness of the individual, the greater the probability of being selected. In the tsp problem, we hope that the shorter the path length of an individual, the better, that is, the shorter the path, the greater the fitness. Here, the article is used to solve the TSP problem based on the genetic algorithm (travel path planning, Python implementation, super detailed, visual , the fitness formula in the results analysis to calculate the fitness

fitness=\frac{1}{distance^{15}}
 

 Individuals with greater fitness are more likely to be selected, using numpy.choice to achieve

idx = np.random.choice(np.arange(self.size_pop), size=self.size_pop, replace=True,
                       p=(self.fitness)/(fitness_sum) )

4. Source code

import numpy as np

class TSP:

    def __init__(self, citys, maxgen=500,
                 size_pop=200, cross_rate=0.8,
                 muta_rate=0.005
                 ):
        self.maxgen = maxgen            # 最大进化次数
        self.size_pop = size_pop        # 种群大小(染色体个数)
        self.cross_rate = cross_rate    # 交叉概率
        self.muta_rate = muta_rate    # 变异概率
        self.citys = citys       # 城市的坐标数据
        self.num = citys.shape[0]    # 城市的个数(染色体长度)
     
     #获得距离矩阵
    def matrix_distance(self):
        self.distance_m=np.zeros((self.num,self.num))
        for i in range(self.num):
            for j in range(self.num):
                self.distance_m[i][j]=np.sqrt((self.citys[i][0]-self.citys[j][0])**2+(self.citys[i][1]-self.citys[j][1])**2)

     #计算某条路径的距离
    def get_total_distance(self,one_path):
        distance=0
        for i in range(self.num-1):
            distance +=self.distance_m[one_path[i]][one_path[i+1]]
        distance += self.distance_m[one_path[-1]][one_path[0]]
        return distance

     #初始化种群
    def generate_population(self):
        path=np.array(range(self.num))
        self.pop=[]
        for i in range(self.size_pop):
            x=path.copy()
            np.random.shuffle(x)    #随机洗牌
            self.pop.append(x)
        
    #交叉互换
    def crossover(self):
        self.new_pop=[]
        for father in self.pop:
            child=father   #初步让子代获得父本染色体
            if np.random.rand()<self.cross_rate:
                #随机选择一个染色体作为母本
                mother_index = np.random.randint(0, self.size_pop)
                mother=self.pop[mother_index]  
                #确定切割点     
                left = np.random.randint(0, self.num-2)
                right = np.random.randint(left + 1, self.num-1)
                mother=mother.tolist()
                father=father.tolist()
                #切割片段
                gene = mother[left:right]
                child1_c = father[right:]+father[:right]
                child1 = child1_c.copy()
                #去除重复基因
                for o in gene:
                    child1_c.remove(o)
                child1[left:right] = gene
                child1[right:] = child1_c[0:len(child1) - right]
                child1[:left] = child1_c[(len(child1) - right):]
                child=np.array(child1)
 
            self.new_pop.append(child)
        self.pop=self.new_pop

    #变异
    def mutation(self):
        for i in range(self.size_pop):
            if np.random.rand() < self.muta_rate:
                child = self.pop[i]
                u = np.random.randint(0,self.num - 2)
                v = np.random.randint(u+1,self.num- 1)
                child_x = child[u+1:v]
                child_x=child_x[::-1]        
                child = np.concatenate((child[0:u+1] , child_x , child[v:]))
                self.pop[i]=child


     #自然选择,种群根据适应度进行更新
    def select(self):
        #计算每条路径的长度,放入列表
        self.dis=[]
        for i in range(self.size_pop):
            self.dis.append(self.get_total_distance(one_path=self.pop[i]))
        #根据路径长度计算每个个体的适应度
        self.fitness=[]
        for i in range(self.size_pop):
            self.fitness.append(1/(self.dis[i]**15))
        #适应度总和
        fitness_sum=0
        for i in range(self.size_pop):
            fitness_sum+=self.fitness[i]
        #根据适应度进行选择,适应度大的被选择概率大
        idx = np.random.choice(np.arange(self.size_pop), size=self.size_pop, replace=True,
                           p=(self.fitness)/(fitness_sum) )
        self.new_pop=[]
        for i in idx:
            self.new_pop.append(self.pop[i])
        self.pop=self.new_pop
        
     #输出当前种群中最优路径
    def result_path(self):
        self.index=np.argmax(self.fitness)
        a="the shortest path is:"
        for i in range(self.num-1):
            a+=str(self.pop[self.index][i])
            a+="-->"
        a+=str(self.pop[self.index][-1])
        print(a)
        print("the total distance is",self.dis[self.index])
        

#主函数进行迭代

def main(citys):
    
    SL=TSP(citys)
    SL.matrix_distance()
    SL.generate_population()
    for i in range (SL.maxgen):
        SL.crossover()
        SL.mutation()
        SL.select()
    SL.result_path()


if __name__ == '__main__':
    citys = np.array([[16.47, 96.10],[16.47, 94.44], [20.09, 92.54],
                     [22.39, 93.37], [25.23, 97.24], [22.00, 96.05], [20.47, 97.02],
                     [17.20, 96.29], [16.30, 97.38], [14.05, 98.12], [16.53, 97.38],
                     [21.52, 95.59], [19.41, 97.13], [20.09, 92.55]])
    main(citys)

Result analysis

If the results are output for each iteration, it can be clearly seen that the paths eventually converge. In fact, the result of each convergence is closely related to the population size size_pop. At the beginning, I set the population size to 200. As a result, although each run converged, the results obtained were different and basically irrelevant, indicating that the results fell into a local optimal solution, not the global optimal solution. The solution is to set size_pop to 500, so that the result error is small and close to the global optimal solution.

In fact, to make the results more intuitive, it is better to visualize the results with a coordinate plot, showing the route. This article only shows the numerical results, so there are two problems. One is that the starting city may be different, such as 8 2 3 6 and 2 3 6 8, and the other is that the order is different, such as 8 2 3 6 and 6 3 2 8. In fact, the result The essence of the route is the same, but the intuitive view is not conducive to the result statistics.

Reference article:

http://t.csdn.cn/eJZmK

http://t.csdn.cn/0fYtK

http://t.csdn.cn/VFPLr

http://t.csdn.cn/2oWZt

Guess you like

Origin blog.csdn.net/m0_73473411/article/details/128967039