当前位置: 首页 > news >正文

遗传算法求解旅行商问题分析

目录

一、问题分析

二、实现步骤

1)初始化种群

2)计算适应度

 3)选择操作

4)交叉操作

5)变异操作

三、求解结果

四、总结 


本文通过一个经典的旅行商问题,详细阐述在实际问题中如何运用遗传算法来进行分析求解。

一、问题分析

       旅行商问题抽象为数学描述为,在平面内有有限个点,寻找一条路径穿过所有点,使路径长度最短。这个问题描述很简单,其实想要求解也很简单,因为途径所有点的路径数量是有限的,我们可以采用穷举法计算每一条路径的距离值,然后得到最短路径。但是采用穷举法的可行性不高,当我们要途径几十个点时,所有路径的总数是n!,这是一个非常庞大的数字,穷举已经不现实。因此我们选择采用其他智能优化算法来求解该问题。

       那么应该如何选择计算方法呢?从上面的分析中我们已经知道,这是一个求解最优解问题,我们还知道这个问题的解是有限范围的,因此我们可以选择遗传算法来求解。下面我们将沿着遗传算法经典步骤,逐步详细说明如何用遗传算法来求解旅行商问题。

二、实现步骤

1)初始化种群

       在初始化种群前,我们首先应考虑如何根据实际问题来设计个体和种群。在旅行商问题中,个体也就是解表征的是一组城市的排列结果,这个解不是单个的数值,而是一个组合。如果我们将每个城市依次编号,那么这个解就是一个N维的向量,向量元素为1到N之间的整数,其中N为城市总数。这个向量非常类似于一个群体中的个体,它已经由基因(城市编号)组成。因此我们可以直接使用这个向量作为群体的个体。初始化群体的代码如下。

       我们定义种群个体数量为100,最大迭代代数为1000,初始种群的大小就是一个100×31(n_cities)的矩阵,n_cities表示个体的基因数,也是城市数量。第一个for循环完成了初始种群的赋值。

    % 遗传算法参数population_size = 100;    % 种群大小max_generations = 1000;    % 最大迭代次数crossover_rate = 0.8;      % 交叉概率mutation_rate = 0.1;       % 变异概率tournament_size = 5;       % 锦标赛选择大小elite_num = 2;             % 精英保留数目% 初始化种群population = zeros(population_size, n_cities);for i = 1:population_sizepopulation(i,:) = randperm(n_cities);end

2)计算适应度

       在旅行商问题中,我们的目标值只有一个,就是路径的距离最短。在得到一个种群之后,我们可以直接计算出种群中每个个体,即每种路径的距离值。显然,距离值越小,适应度越大。因此,可以使用距离的导数来表征个体适应度的大小。具体实现代码如下。distances为已经计算好的两两城市之间的距离,此处直接提出相加即可。

        % 计算适应度fitness = zeros(population_size, 1);for i = 1:population_sizefitness(i) = 1 / calculate_total_distance(population(i,:), distances);end
% 计算路径总长度
function total_dist = calculate_total_distance(path, distances)n = length(path);total_dist = 0;for i = 1:n-1total_dist = total_dist + distances(path(i), path(i+1));endtotal_dist = total_dist + distances(path(end), path(1));
end

 3)选择操作

       此处采用锦标赛法选择需要继承的父代。该方法的思想是随机从种群中抽取部分个体,然后将这部分个体中适应度最高的个体传递给下一代。此处直接选择两个父代的个体,以便直接进行后续的交叉和变异操作。

% 锦标赛选择父代
parent1 = tournament_selection(population, fitness, tournament_size);
parent2 = tournament_selection(population, fitness, tournament_size);
% 锦标赛选择
function selected = tournament_selection(population, fitness, tournament_size)candidates = randperm(size(population,1), tournament_size);[~, idx] = max(fitness(candidates));selected = population(candidates(idx), :);
end

4)交叉操作

        直接对选出的两个父代个体执行交叉操作。此处采用的交叉方法为顺序交叉法,其算法思想如下图所示。首先随机得到一个交叉区域,将个体P1中该区域的元素赋给新个体child1的对应区域位置,然后将P2以交叉区域后边界分为前后两段,并将前后两段互换位置得到一个新的片段,当然这个片段中删除了交叉区域中已经存在的元素,然后再将这个片段分别赋给child1的两端,不要覆盖交叉区域。这样就得到了交叉后的第一个新个体,然后交换P1和P2角色,执行上述操作,得到child2第二个个体,这样交叉操作就完成了。

% 交叉
if rand < crossover_rate[child1, child2] = ox_crossover(parent1, parent2);
elsechild1 = parent1;child2 = parent2;
end
% 顺序交叉 (OX)
function [child1, child2] = ox_crossover(parent1, parent2)n = length(parent1);points = sort(randperm(n, 2));start = points(1);end_ = points(2);% 子代1segment1 = parent1(start:end_);remaining = [];for i = [end_+1:n, 1:end_]city = parent2(i);if ~ismember(city, segment1)remaining = [remaining, city];endendchild1 = zeros(1, n);child1(start:end_) = segment1;child1([1:start-1, end_+1:n]) = remaining(1:n - (end_ - start + 1));  % 子代2segment2 = parent2(start:end_);remaining = [];for i = [end_+1:n, 1:end_]city = parent1(i);if ~ismember(city, segment2)remaining = [remaining, city];endendchild2 = zeros(1, n);child2(start:end_) = segment2;child2([1:start-1, end_+1:n]) = remaining(1:n - (end_ - start + 1));
end

5)变异操作

       此处变异使用简单的交换操作,即交换个体中任意两个位置的元素。具体实现代码如下,首先随机产生一个随机数,判断是否满足可变异概率,然后在随机生成两个位置,将这两个位置的元素进行交换即可。

% 变异
child1 = mutate(child1, mutation_rate);
child2 = mutate(child2, mutation_rate);
% 变异(交换)
function mutated = mutate(individual, mutation_rate)mutated = individual;if rand < mutation_raten = length(individual);pos = randperm(n, 2);mutated(pos(1)) = individual(pos(2));mutated(pos(2)) = individual(pos(1));end
end

三、求解结果

程序完整实现代码在这里。最终求解结果如下图所示,得到的最优距离为15598。

四、总结 

       通过遗传算法求解旅行商问题流程可知,遗传算法的各项具体操作实现方法不是固定不变的,需要根据实际问题分析处理。即使使用本文提供的实际程序计算,每次迭代计算得到的结果也是不一样的。旅行商问题本质上是一个排列问题,且越接近最优解时,解相邻元素的排列顺序越接近。根据这一特性,我们在进行交叉、变异产生新个体时就需要设计合适的算法实现,减小对排列顺序大幅度调整的概率。因此,我们以非常小的概率控制变异发生,在交叉过程中使用分段交叉,尽量保持相邻元素的排列顺序。总之,在遗传算法产生新的子代群体过程中,我们应尽量避免产生大量与上一代差异较大的个体,当然也尽可能保留少量差异大的个体,使种群始终向最优解方向缓慢进化,避免算法不收敛或陷入局部最优解中。

http://www.xdnf.cn/news/470089.html

相关文章:

  • Python内存管理:赋值、浅拷贝与深拷贝解析
  • Mendix 连接 MySQL 数据库
  • Linux动态库热加载驱动插件机制-示例
  • 国标GB28181视频平台EasyGBS助力智慧医院打造全方位视频监控联网服务体系
  • QML元素 - MaskedBlur
  • 力扣-236.二叉树的最近公共祖先
  • Elasticsearch 常用语法手册
  • 格恩朗椭圆齿轮流量计 工业流量测量的可靠之钥
  • MySQL库的操作
  • 【笔记】CosyVoice 模型下载小记:简单易懂的两种方法对比
  • vacuum、vacuum full的使用方法及注意事项
  • “禁塑行动·我先行”环保公益项目落地宁夏,共筑绿色生活新篇章
  • 4、前后端联调文生文、文生图事件
  • 趋势跟踪策略的回测
  • AI Agent开发第67课-彻底消除RAG知识库幻觉-文档分块全技巧(1)
  • pgsql14自动创建表分区
  • SpringBoot 自动装配流程
  • [Java实战]Spring Boot 3实现 RBAC 权限控制(二十五)
  • SpringBoot项目使用POI-TL动态生成Word文档
  • 去年开发一款鸿蒙Next Os的window工具箱
  • 软考软件评测师——软件工程之系统维护
  • ADS1220高精度ADC(TI)——应用 源码
  • 采用sherpa-onnx 实现 ios语音唤起的调研
  • 每周靶点:NY-ESO-1、GPC3、IL27分享
  • Linux操作
  • Oracle APEX IR报表列宽调整
  • [ctfshow web入门] web75
  • 运维实施30-FTP服务
  • 欧拉计划 Project Euler 73(分数有范围计数)题解
  • ABP User Interface-Angular UI中文详解