遗传算法(Genetic Algorithm)实现TSP问题

一、遗传算法的基本思想


遗传算法(Genetic Algorithm, GA)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。

遗传算法(Genetic Algorithm, GA)起源于对生物系统所进行的计算机模拟研究。它是模仿自然界生物进化机制发展起来的随机全局搜索和优化方法,借鉴了达尔文的进化论和孟德尔的遗传学说。其本质是一种高效、并行、全局搜索的方法,能在搜索过程中自动获取和积累有关搜索空间的知识,并自适应地控制搜索过程以求得最佳解。

达尔文进化论的主要观点是:物竞天择,适者生存。遗传算法(Genetic Algorithm)的基本思想就是模仿自然进化过程,通过对群体中具有某种结构形式的个体进行遗传操作,从而生成新的群体,逐渐逼近最优解。在求解过程中设定一个固定规模的种群,种群中的每个个体都表示问题的一个可能解,个体适应环境的程度用适应度函数判断,适应度差的个体被淘汰,适应度好的个体得以继续繁衍,繁衍的过程中可能要经过选择、交叉、变异,形成新的族群,如此往复,最后得到更多更好的解。

二、遗传算法的主要步骤


(1)编码:将问题的候选解用染色体表示,实现解空间向编码空间的映射过程。遗传算法不直接处理解空间的决策变量,而是将其转换成由基因按一定结构组成的染色体。编码方式有很多,如二进制编码、实数向量编码、整数排列编码、通用数据结构编码等等。本文将采用二进制编码的方式,将十进制的变量转换成二进制,用0和1组成的数字串模拟染色体,可以很方便地实现基因交叉、变异等操作。

(2)种群初始化:产生代表问题可能潜在解集的一个初始群体(编码集合)。种群规模设定主要有以下方面的考虑:从群体多样性方面考虑,群体越大越好,避免陷入局部最优;从计算效率方面考虑,群体规模越大将导致计算量的增加。应该根据实际问题确定种群的规模。产生初始化种群的方法通常有两种:一是完全随机的方法产生;二是根据先验知识设定一组必须满足的条件,然后根据这些条件生成初始样本。

(3)计算个体适应度:利用适应度函数计算各个个体的适应度大小。适应度函数(Fitness Function)的选取直接影响到遗传算法的收敛速度以及能否找到最优解,因为在进化搜索中基本不利用外部信息,仅以适应度函数为依据,利用种群每个个体的适应程度来指导搜索。

(4)进化计算:通过选择、交叉、变异,产生出代表新的解集的群体。选择(selection):根据个体适应度大小,按照优胜劣汰的原则,淘汰不合理的个体;交叉(crossover):编码的交叉重组,类似于染色体的交叉重组;变异(mutation):编码按小概率扰动产生的变化,类似于基因突变。

(5)解码:末代种群中的最优个体经过解码实现从编码空间向解空间的映射,可以作为问题的近似最优解。这是整个遗传算法的最后一步,经过若干次的进化过程,种群中适应度最高的个体代表问题的最优解,但这个最优解还是一个由0和1组成的数字串,要将它转换成十进制才能供我们理解和使用。
以上文字原文链接:https://blog.csdn.net/u011125673/article/details/93393041

三、几点说明

1.问题的表示


路径表示是表示旅程对应的基因编码的最自然,最简单的表示方法。它在编码,解码,存储过程中相对容易理解和实现。例如:旅程(5-1-7-8-9-4-6-2-3)可以直接表示为(5 1 7 8 9 4 6 2 3)

2.轮盘赌选择算法(Roulette Wheel Selection)


它模拟博彩游戏的轮盘赌。一个轮盘被划分为N个扇形表示种群的一个染色体,而每个扇形的面积与它所表示的染色体的适应值成正比,为了选择种群中的个体,设想有一个指针指向轮盘,转动轮盘,当轮盘停止后,指针所之乡的染色体被选择。因为一个染色体的适应值越大表示该染色体的扇形面积就越大,因此它被选择的可能性也就越大。

3.顺序交叉概念(后面代码会用到)

 上图原文链接:遗传算法解决TSP问题_里昂科科的博客-CSDN博客

4.变异操作

在旅行路线上随机选择2个城市进行交换

5.算法的局限性

 当问题规模n比较小时,得到的一般都是最优解;当规模比较大时,一般只能得到近似解。这时可以通过增大种群大小和增加最大遗传代数或者增大变异几率使得优化值更接近最优解。

6.关于遗传算法所涉及的变量,在代码中均有说明,关于遗传算法更多的细节和背景说明,可以看其他博主,这里主要用代码展示其实现过程

 四、完整代码

%使用遗传算法实现TSP问题
clc,clear all;close all;
%随机化城镇
N=20;    %城镇数量
C=floor(rand(N,2).*100.+1);%随机化城市位置(小概率的两城市重合不考虑)
distance=dis_arr(C,N);  %城市距离矩阵
distance=distance+distance';
srt=[];%初始旅行路线
for i=1:N
    srt=[srt i];
end
subplot(221);
grid on;
title('原始TSP图像');
xlabel('x');
ylabel('y');
 netplot(C,srt,N);
%遗传算法数据初始化;
max_Nc=1000;%进化代数
sizepop=60;%种群规模
crs_p=0.8;  %交叉概率
var_p=N/150; %变异概率
individual=struct('fitness',[],'chrom',[]);%存储适应值(路线长度)和城市路线,这里适应值越小越好
best_fitness=[max_Nc,1];
%best_chrom=[max_Nc,1];
%----------------种群初始化---------------------
for i=1:sizepop
   individual.chrom(i,:)=randperm(N); 
   tra_path=individual.chrom(i,:);
   individual.fitness(i)=cpt_path_len(distance,tra_path,N);
end
[min_fitness,min_index]=min(individual.fitness); %此处将路线总路程当作适应值,按最小看
min_path=individual.chrom(min_index,:);
%---------------种群开始进化---------------------
for Nc=1:max_Nc
    %种群选择
    individual=select(individual,sizepop);
    [min_fitness,min_index]=min(individual.fitness); %此处将路线总路程当作适应值,按最小看
    min_path=individual.chrom(min_index,:);
    % 种群交叉
    individual=cross(individual,sizepop,crs_p,N,distance);  
    %种群变异
    individual=variation(individual,sizepop,var_p,N,distance);
    %记录每一次迭代的适应值和旅行路程
    
    [min_fitness1,min_index1]=min(individual.fitness); %此处将路线总路程当作适应值,按最小看
    %将遗传前最好的个体留下来
    if(min_fitness<min_fitness1)
        individual.fitness(min_index1)=min_fitness;
        individual.chrom(min_index1,:)=min_path;
    end
    min_path=individual.chrom(min_index1,:);  
    best_fitness(Nc,1)=min_fitness;
end
subplot(222);
netplot(C,min_path,N);
grid on
title('TSP问题优化结果');
xlabel('x');
ylabel('y');
subplot(223);
grid on
title('每次迭代总路程');
plot(best_fitness);
xlabel('迭代次数');
ylabel('路线长度');
%---------------函数定义-----------------------
function var_individual=variation(individual,sizepop,var_p,N,distance)
    var_individual=individual;
    [~,index]=min(individual.fitness);
    for i=1:sizepop
        if i==index
            continue;
        end
        if(var_p>rand)
            p1=floor(rand*N+1);
            p2=floor(rand*N+1);
            while p1==p2
                p1=floor(rand*N+1);
                p2=floor(rand*N+1);
            end      %随机选2个位置进行交换      
            tmp=var_individual.chrom(i,p1);
            var_individual.chrom(i,p1)=var_individual.chrom(i,p2);            
            var_individual.chrom(i,p2)=tmp;
            var_individual.fitness(i)=cpt_path_len(distance,var_individual.chrom(i,:),N); %计算适应度           
        end
    end
end
function crs_individual=cross(individual,sizepop,crs_p,N,distance) %交叉运算
    crs_individual=individual;
    rot=floor(sizepop/2)*2;  
    for i=1:2:rot        
       pick=rand;
       if(crs_p>pick)
           start=randi([1,N-ceil(N/2)]);
           num=randi([0,ceil(N/2)]);
           cut1=crs_individual.chrom(i,start:num+start);
           cut2=crs_individual.chrom(i+1,start:num+start);
           crschrom1=crs_individual.chrom(i,:);
           crschrom2=crs_individual.chrom(i+1,:);
           schrom1=[];
           schrom2=[];
           for j=1:N
              if (length(find(cut1==crschrom2(j)))==0) %采用顺序交叉,看文章图示说明
                  schrom2=[schrom2 crschrom2(j)];              
              end
              if (length(find(cut2==crschrom1(j)))==0) 
                  schrom1=[schrom1 crschrom1(j)];                 
              end
           end           
           n=1;m=1;
           newchrom1=[];
           newchrom2=[];
           while(n<=N)
               if(n==start)
                  newchrom1=[newchrom1 cut2];
                  newchrom2=[newchrom2 cut1];
                  n=n+num+1;
               else
                   newchrom1=[newchrom1 schrom1(m)];
                   newchrom2=[newchrom2 schrom2(m)];
                   m=m+1;
                   n=n+1;
               end              
           end
           crs_individual.chrom(i,:)=newchrom1;
           crs_individual.fitness(i)=cpt_path_len(distance,newchrom1,N);
           crs_individual.chrom(i+1,:)=newchrom2;
           crs_individual.fitness(i+1)=cpt_path_len(distance,newchrom2,N);
       end     
    end
end
function slt_individual=select(individual,sizepop) %选择运算
    p1=sum(1./individual.fitness);
    p=1./individual.fitness./p1;
    p=cumsum(p);%以下开始采用轮盘赌选择法
    slt_individual=individual;    
    for i=1:sizepop
        select1=find(p>rand);        
        slt_individual.fitness(i)=individual.fitness(select1(1));
        slt_individual.chrom(i,:)=individual.chrom(select1(1),:);        
    end
    %将上次适应度前5%的精英留下
    num=ceil(sizepop/20);
    for i=1:num
       [last_minfit,index]=min(individual.fitness );
       last_minpath=individual.chrom(index,:);
       individual.fitness(index)=inf;
       [~,maxindex]=max(slt_individual.fitness);
       slt_individual.fitness(maxindex)=last_minfit;
        slt_individual.chrom(maxindex,:)=last_minpath;
    end    
end
function path_len=cpt_path_len(distance,path,N) %计算路径长度
    path_len=0;
    for i=1:N-1
        path_len=path_len+distance(path(i),path(i+1));
    end
    path_len=path_len+distance(path(N),path(1));
end
function len_arr=dis_arr(C,N) %求距离矩阵
len_arr=zeros(N);
    for i=1:N-1
       for j=i+1:N
           len_arr(i,j)=sqrt((C(i,1)-C(j,1))^2+(C(i,2)-C(j,2))^2);
       end
    end
end
function netplot(C,path,N) %画图
    hold on;
    for i=1:N-1
        k=path(i);
        m=path(i+1);
        plot(C(k,1),C(k,2),'ro');
        line([C(k,1) C(m,1)],[C(k,2) C(m,2)],'Color','green');
    end
    k=path(N);
    m=path(1);
    plot(C(k,1),C(k,2),'ro');
    line([C(k,1) C(m,1)],[C(k,2) C(m,2)],'Color','green');
end

五、结果展示

 代码为自己独立编写完成,可能还有不少值得优化的地方哦~

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Python实现遗传算法解决TSP旅行商问题的示例代码: ```python import random # 城市坐标 city_pos = [(60, 200), (180, 200), (80, 180), (140, 180), (20, 160), (100, 160), (200, 160), (140, 140), (40, 120), (100, 120), (180, 100), (60, 80), (120, 80), (180, 60), (20, 40), (100, 40), (200, 40), (20, 20), (60, 20), (160, 20)] # 种群大小 POP_SIZE = 500 # 城市数量 ITY_COUNT = len(city_pos) # 交叉概率 CROSS_RATE = 0.1 # 变异概率 MUTATION_RATE = 0.02 # 代数 N_GENERATIONS = 500 # 计算两个城市之间的距离 def distance(city1, city2): return ((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2) ** 0.5 # 计算一条路径的总距离 def get_fitness(path): distance_sum = 0 for i in range(CITY_COUNT - 1): distance_sum += distance(city_pos[path[i]], city_pos[path[i+1]]) distance_sum += distance(city_pos[path[-1]], city_pos[path[0]]) return 1 / distance_sum # 初始化种群 def init_population(): population = [] for i in range(POP_SIZE): path = list(range(CITY_COUNT)) random.shuffle(path) population.append(path) return population # 选择 def select(population, fitness): idx = random.randint(0, POP_SIZE - 1) for i in range(POP_SIZE): if random.random() < fitness[i] / fitness.sum(): idx = i break return population[idx] # 交叉 def crossover(parent1, parent2): if random.random() < CROSS_RATE: child = [-1] * CITY_COUNT start = random.randint(0, CITY_COUNT - 1) end = random.randint(start, CITY_COUNT - 1) child[start:end+1] = parent1[start:end+1] for i in range(CITY_COUNT): if parent2[i] not in child: for j in range(CITY_COUNT): if child[j] == -1: child[j] = parent2[i] break return child else: return parent1 # 变异 def mutate(child): if random.random() < MUTATION_RATE: idx1, idx2 = random.sample(range(CITY_COUNT), 2) child[idx1], child[idx2] = child[idx2], child[idx1] return child # 遗传算法主函数 def genetic_algorithm(): population = init_population() for generation in range(N_GENERATIONS): fitness = [get_fitness(path) for path in population] best_path = population[fitness.index(max(fitness))] print("Generation:", generation, "| Best path length:", 1 / max(fitness)) new_population = [best_path] for i in range(POP_SIZE - 1): parent1 = select(population, fitness) parent2 = select(population, fitness) child = crossover(parent1, parent2) child = mutate(child) new_population.append(child) population = new_population return best_path # 运行遗传算法 best_path = genetic_algorithm() print("Best path:", best_path) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值