以下内容为卢朓老师在B站进行的分享,可以作为学习参考:
在科学与工程的广阔领域里,优化问题无处不在,它们挑战着我们的智慧和计算能力。其中,旅行商问题(TSP,Traveling Salesman Problem)便是一个经典而富有挑战性的优化难题。在这个问题中,一位旅行商需要访问一系列城市,并找到一条从起点出发,经过所有城市后返回起点的最短路径。随着城市数量的增加,问题的复杂度呈爆炸式增长,寻找最优解变得异常困难。
旅行商问题的挑战
旅行商问题的难度在于其计算量的巨大。当城市数量较少时,我们还可以尝试通过穷举所有可能的路径来找到最短路径。但是,随着城市数量的增加,可能的路径数量呈指数级增长,穷举法变得不切实际。例如,当有10个城市时,可能的路径数量就已经达到了3628800条!对于更多的城市,计算量更是惊人。
模拟退火算法:大自然的启示
为了应对这一挑战,科学家们开发了各种启发式搜索算法,其中模拟退火算法便是一种非常有效的方法。这种算法借鉴了物理学中固体退火的原理。正如金属在加热后再缓慢冷却的过程中,原子会逐渐排列成有序的结构,从而达到最低的能量状态,模拟退火算法也通过模拟这一过程来寻找问题的最优解。
在算法开始时,我们设定一个较高的“温度”,并随机生成一个可能的解作为当前解。然后,算法会在这个解的“邻域”内随机探索新的解。如果新解比当前解更优(即路径更短),我们就接受这个新解。如果新解不如当前解,我们也有一定的概率接受它,这个概率随着温度的降低而逐渐减小。这个过程就像金属在退火过程中,即使有时原子会排列成能量较高的状态,但随着温度的降低,它们最终还是会趋于最低能量状态。通过不断地探索和优化,模拟退火算法能够逐渐找到越来越接近最优解的解决方案。
代码实现:模拟退火求解旅行商问题
接下来,我们将通过一段北太天元代码来展示如何使用模拟退火算法求解旅行商问题。这段代码首先生成了一系列随机分布的城市(或点),并计算了每对城市之间的距离。然后,它使用模拟退火算法来找到一条近似最短路径。
%北太天元 用模拟退火法 求解 旅行商 问题% 生成num_points个随机点的经纬度 num_points = 10; longitudes = rand(num_points, 1) * 360 - 180; % 随机经度在-180到180之间 latitudes = rand(num_points, 1) * 180 - 90; % 随机纬度在-90到90之间 % 将经纬度保存到sj.txt文件中 fileID = fopen('sj.txt', 'w'); for i = 1:num_points fprintf(fileID, '%f\t%f\n', longitudes(i), latitudes(i)); end fclose(fileID); disp('sj.txt文件已生成,包含10个随机点的经纬度。'); % 读取数据 sj0 = readmatrix('sj.txt'); x = sj0(:,[1:2:end]); y = sj0(:,[2:2:end]); sj = [x, y]; d1 = [70, 40]; sj = [d1; sj; d1]; sj = deg2rad(sj); % 初始化距离矩阵 d = zeros(num_points+2); % 计算距离矩阵 for i = 1:num_points+1 for j = i+1:num_points+2 d(i,j) = 6370 * acos(cos(sj(i,1)-sj(j,1)) .* cos(sj(i,2)) .* cos(sj(j,2)) + sin(sj(i,2)) .* sin(sj(j,2))); end end d = d + d'; % 初始化路径和路径长度 path = 1:num_points+2; long = sum(d(sub2ind(size(d),path(1:end-1),path(2:end)))); % 模拟退火算法参数 e = 1e-10; L = 20000; at = 0.8; T = 1; % 模拟退火过程 for k = 1:L for i = 1:num_points temp = path; m = randperm(num_points,2); temp( [m(1)+1,m(2)+1] ) = path([m(2)+1,m(1)+1]); % 交换路径中的两个点 temp_long = sum(d(sub2ind(size(d),temp(1:end-1),temp(2:end)))); if temp_long < long || (long - temp_long) / T > log(rand) path = temp; long = temp_long; end end T = T * at; if T < e break; end end % 输出结果 xx = sj(path,1); yy = sj(path,2); plot(rad2deg(xx), rad2deg(yy), '-o'); xlabel('经度'); ylabel('纬度'); hold on;scatter(rad2deg(xx(1)),rad2deg(yy(1)),'r')for i = 1:length(path) text(rad2deg(xx(i)), rad2deg(yy(i)), sprintf('%d', i), 'Color', 'red'); end hold off;disp(['总路径长度为:', num2str(long), ' km']); disp(['所需时间为:', num2str(long / 1000), ' 小时']); % 假设飞机速度为1000km/h
这段代码通过模拟退火算法的不断迭代和优化,最终输出了一条近似最短路径以及总路径长度。尽管由于问题的复杂性和计算量的限制,我们无法保证找到的解是最优的,但模拟退火算法通常能够给出一个非常接近最优解的解决方案。
结语
模拟退火算法作为一种启发式搜索算法,在解决旅行商问题等复杂优化问题方面展现出了强大的能力。通过模拟物理退火过程,它能够在庞大的解空间中高效地探索并找到近似最优解。尽管随着城市数量的增加,旅行商问题的计算量仍然是一个巨大的挑战,但模拟退火算法为我们提供了一种有效的应对策略。在未来的研究和应用中,我们可以进一步优化和改进这一算法,以应对更加复杂和庞大的优化问题。
p.s. 这里还假设飞机和目标点都是地球表面, 而且飞机总是贴着地球表面飞行,因还涉及到地球表面上的两个位置点之间的距离(不是三维空间的直线距离,而是贴着球面两点连线之间的最短距离, 也就是测地线的长度), 因此中学生朋友还请参考 北太天元科普: 测地线 - 哔哩哔哩 (bilibili.com)
p.s. 上面的代码用文心一言进行解读
这段代码是一个使用模拟退火算法(Simulated Annealing)来解决旅行商问题(Traveling Salesman Problem, TSP)的MATLAB(或者北太天元)脚本。旅行商问题是一个经典的组合优化问题,目标是找到一条经过所有城市并返回起点的最短路径。下面是对代码的详细解读:
生成随机点:
num_points = 10; 定义了点的数量为10。
longitudes 和 latitudes 分别生成了10个随机经度和纬度,经度范围在-180到180之间,纬度范围在-90到90之间。
保存点到文件:
将生成的经纬度保存到sj.txt文件中,每行一个点,经度在前,纬度在后。
读取数据并初始化:
读取sj.txt文件中的数据,并将其转换为弧度制(deg2rad),同时将出发点和终点设置为(70, 40)。
计算距离矩阵:
初始化一个num_points+2维的零矩阵d(包括出发点和终点)。
使用球面距离公式计算每对点之间的距离,并填充到距离矩阵d中。
绘图显示:
使用scatter函数在地图上显示所有的点,并使用text函数在每个点旁边显示其编号(除了出发点和终点)。
初始化路径和路径长度:
初始化路径为按顺序访问每个点,并计算这个路径的总长度。
模拟退火算法:
设置模拟退火算法的参数,包括温度T、冷却率at、结束温度e和迭代次数L。
在每次迭代中,随机选择两个点交换它们在路径中的位置,计算新路径的长度。
如果新路径比当前路径短,或者满足一定的概率条件,则接受新路径。
更新温度,如果温度低于结束温度,则结束迭代。
输出结果:
绘制最终路径的地图,并在每个点旁边显示其编号。
显示总路径长度和假设飞机速度为1000km/h时的所需时间。
总的来说,这段代码展示了如何使用模拟退火算法来解决旅行商问题,包括生成随机点、计算距离矩阵、使用模拟退火算法寻找最短路径,并绘制结果地图和显示路径长度和所需时间。