数模(一)TSP问题

引用之前ACM博客状压DP一文对于TSP问题的描述:
经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需经过所有城市后,回到起点。应该如何选择路线,使得总行程距离最短。从图论的观点来看是,该问题实质是在一个有权完全无向图中找一个权值最小的哈密顿回路。
大部分TSP问题都是NP-hard问题,即没有多项式时间复杂度的算法,一般倾向于剪枝搜索或者状压DP的方式解决。这类问题地点数量通常很小。

以上主要考虑的是TSP问题精确解的求法,在数模中我们可以求近似解,从中筛选较优解。

DP

首先回顾一下之前状压DP的方法:
定义状态: f [ s t a t u s ] [ j ] :表示状态为 s t a t u s (即已经过 s t a t u s 所表示点集),最后访问的是节点 j 的最短距离 f[status][j]: 表示状态为 status (即已经过 status 所表示点集),最后访问的是节点 j 的最短距离 f[status][j]:表示状态为status(即已经过status所表示点集),最后访问的是节点j的最短距离
递推方程: f [ s t a t u s ] [ j ] = m i n k ∈ s t a t u s & & j ∉ s t a t u s ( f [ s t a t u s − ( 1 < < j ) ] [ k ] + d i s [ k ] [ j ] ) f[status][j] = min_{k{\in}status\&\&j{\notin}status}(f[status-(1<<j)][k]+dis[k][j]) f[status][j]=minkstatus&&j/status(f[status(1<<j)][k]+dis[k][j])
也可以写作: f [ s t a t u s ∣ ( 1 < < k ) ] [ k ] = m i n k ∉ s t a t u s ( f [ s t a t u s ] [ j ] + d i s [ j ] [ k ] ) f[status | (1 << k)][k] = min_{k{\notin}status}(f[status][j] + dis[j][k]) f[status(1<<k)][k]=mink/status(f[status][j]+dis[j][k])
最终: a n s = m i n ( f [ ( 1 < < n ) − 1 ] [ i ] + d i s [ i ] [ s t ] ) ans=min(f[(1<<n)-1][i]+dis[i][st]) ans=min(f[(1<<n)1][i]+dis[i][st])

关键点:1. 状态压缩已访问节点,实际上可以直接用数组表示集合,这里通过状压的方法用一个数表示集合 2. 转移状态由已访问节点集合以及最后访问节点确定

C++程序实现:

// 参照第二个递推方程
for (int i = 1; i < (1 << n); i++) {	// i即是status,枚举状态
    for (int j = 0; j < n; j++) {	// 枚举
        if (!((i >> j) & 1)) continue;	// 枚举状态f[i]可能的最后访问节点
        for (int k = 0; k < n; k++) {   // j -> k,k为状态f[i|(1<<k)]最后访问节点
            if ((i >> k) & 1) continue;
            f[i | (1 << k)][k] = min(f[i | (1 << k)][k], f[i][j] + g[j + 1][k + 1]);
        }
    }
}

时间复杂度: O ( n 2 ∗ 2 n ) O(n^2 * 2^n) O(n22n)

一个经典的近似解做法

P ≠ N P P\neq{NP} P=NP情况下TSP问题不存在多项式时间的常数近似。但是,TSP问题在度量空间是可以有多项式时间的常数近似。所谓度量空间,就是空间中的任意3点满足三角不等式。

  1. 求出图G的最小生成树T,其权值和为 W ( T ) W(T) W(T);
  2. 将T的边double一下得到欧拉图G’(每个点的度数均为偶数);
  3. 求G’欧拉回路,根据得到的点序列,跳过重复的点便得到一个哈密顿回路C。

因为满足三角不等式,所以 W ( C ) ≤ 2 W ( T ) W(C)\leq2W(T) W(C)2W(T) ,而 W ( T ) ≤ O P T W(T){\leq}OPT W(T)OPT ,所以 W ( C ) ≤ 2 O P T W(C)\leq2OPT W(C)2OPT ,这意味着该近似解不会超过最优解的2倍。

我们发现:由于G’是一棵树的边double而得,对G’进行dfs遍历,递归时走原边,回溯时走复制边,跳过重复的点得到实际就是树的前/中/后序序列

请添加图片描述
求最小生成树T:
求最小生成树T
求前序序列L:
求前序序列L

时间复杂度:通过类似Dijkstra算法中用堆存储所有边,依次弹出所有边并判断一下是否是由已访问点连向未访问点的,可以保证所有边访问一次,这样的时间复杂度为 O ( m l o g m ) O(mlogm) O(mlogm) ,而 1 / 2 n ∗ n ≥ m 1/2n*n\geq{m} 1/2nnm ,因此可以表示为 O ( n 3 l o g n ) O(n^3logn) O(n3logn)

欧拉回路

定义及性质参考:https://zhuanlan.zhihu.com/p/44055074
求欧拉回路的Fleury算法:
设G为一无向欧拉图,
(1)任取G中一顶点 v 0 v_0 v0,令 p 0 = v 0 p_0=v_0 p0=v0;
(2)假设沿 p i = v 0 e 1 v 1 e 2 v 2 . . . e i v i p_i=v_0e_1v_1e_2v_2...e_iv_i pi=v0e1v1e2v2...eivi走到顶点 v i v_i vi,按下面方法从 E ( G ) − { e 0 , e 1 , e 2 , . . . e i } E(G)-\{e_0,e_1,e_2,...e_i\} E(G){e0,e1,e2,...ei}中选择 e i + 1 e_{i+1} ei+1:
(a) e i + 1 e_{i+1} ei+1 v i v_{i} vi关联;
(b)除非无别的边供选择,否则 e i + 1 e_{i+1} ei+1不应该是 E ( G ) − { e 0 , e 1 , e 2 , . . . e i } E(G)-\{e_0,e_1,e_2,...e_i\} E(G){e0,e1,e2,...ei}中的桥。
(3)(2)不能进行时算法停止。

割边:设无向图 G ( V , E ) G(V, E) G(V,E)为连通图,若边集 E 1 ⊆ E E_1⊆E E1E,在图G 中删除 E 1 E_1 E1中所有的边后得到的子图是不连
通的,而删除了 E 1 E_1 E1的任一真子集后得到的子图是连通图,则称 E 1 E_1 E1是G的一个割边集。若一条边
构成一个割边集,则称该边为割边,或桥。

修改圈近似算法

首先求一个 Hamilton 圈 C ,然后适当修改 C 以得到具有更小权的新 Hamilton 圈。修改的方法为改良圈算法。设初始圈为 C = v 1 v 2 . . . v n v 1 C = v_1v_2...v_nv_1 C=v1v2...vnv1
(1)对于 1 ≤ i < i + 1 < j ≤ n 1\leq{i}<i+1<j\leq{n} 1i<i+1<jn ,构造新的 Hamilton 圈
C i j = v 1 v 2 . . . v i v j v j − 1 v j − 2 . . . v i + 1 v j + 1 v j + 2 . . . v n v 1 C_{ij}=v_1v_2...v_iv_jv_{j-1}v_{j-2}...v_{i+1}v_{j+1}v_{j+2}...v_nv_1 Cij=v1v2...vivjvj1vj2...vi+1vj+1vj+2...vnv1
w ( v i v j ) + w ( v i + 1 v j + 1 ) < w ( v i v i + 1 ) + w ( v j v j + 1 ) w(v_iv_j)+w(v_{i+1}v_{j+1})<w(v_iv_{i+1})+w(v_{j}v_{j+1}) w(vivj)+w(vi+1vj+1)<w(vivi+1)+w(vjvj+1) ,则以 C i j C_{ij} Cij 代替 C,称前者为改良圈。
(2)转(1),直至无法进行改进,停止。
该算法的结果几乎可以肯定不是最优的,会陷入局部最优解,但是改良圈的思想可以和智能算法结合
,进行优化。

参考:

数学建模算法与应用(第2版) 司守奎 孙兆亮
https://zhuanlan.zhihu.com/p/315210267 讨论度量空间下TSP问题的两个近似算法
https://www.jianshu.com/p/d1e004bdea8d 近似解python实现
https://www.isolves.com/it/cxkf/sf/2020-07-19/24491.html SOM(竞争神经网络)算法求解
https://zhuanlan.zhihu.com/p/467270686 TSP求解算法集合

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 以下是一个使用遗传算法的 Python 代码示例,用于求解旅行商问题TSP): ```python import random import numpy as np # 距离矩阵 dist = [[0, 2, 9, 10], [1, 0, 6, 4], [15, 7, 0, 8], [6, 3, 12, 0]] # 遗传算法参数 pop_size = 100 generations = 1000 # 初始种群 pop = [np.random.permutation(len(dist)) for _ in range(pop_size)] for generation in range(generations): # 计算种群中每个个体的适应度 fitness = [np.sum([dist[ind1][ind2] for ind1, ind2 in zip(ind, ind[1:]+ind[:1])]) for ind in pop] # 选择父代 parents = np.random.choice(np.arange(pop_size), size=pop_size, replace=True, p=fitness/np.sum(fitness)) # 交叉 offspring = [np.random.permutation(np.concatenate((pop[i][:int(len(dist)/2)], pop[j][int(len(dist)/2):]))) for i, j in zip(parents[::2], parents[1::2])] # 变异 offspring = [np.random.permutation(ind) for ind in offspring] # 更新种群 pop = offspring # 找出最优解 best_ind = pop[np.argmin(fitness)] best_fitness = np.min(fitness) print(best_ind) print(best_fitness) ``` 该代码使用遗传算法,通过进化几代种群来求解 TSP 问题。距离矩阵 `dist` 需要提前给定。种群大小、遗传代数也可以根据需要调整。 请注意,这只是一种求解 TSP 的方法,并不能保证每次都能找到最优解。 ### 回答2: TSP问题(Traveling Salesman Problem,旅行商问题)是一个经典的组合优化问题,可以用来解决一旦旅行商要在多个城市中旅行一次,如何选择最短路线的问题。 下面是一个使用贪心算法求解TSP问题的Python代码: ```python import numpy as np def tsp(graph, start): num_cities = len(graph) visited = [False] * num_cities visited[start] = True route = [start] total_distance = 0 while len(route) < num_cities: curr_city = route[-1] min_distance = float('inf') next_city = None for i in range(num_cities): if not visited[i] and graph[curr_city][i] < min_distance: min_distance = graph[curr_city][i] next_city = i route.append(next_city) visited[next_city] = True total_distance += min_distance route.append(start) total_distance += graph[route[-2]][start] return route, total_distance if __name__ == '__main__': # 定义一个城市之间的距离矩阵 graph = np.array([[0, 2, 9, 10], [1, 0, 6, 4], [15, 7, 0, 8], [6, 3, 12, 0]]) # 选择起始城市为0 start_city = 0 optimal_route, total_distance = tsp(graph, start_city) print("最优路线:", optimal_route) print("最短距离:", total_distance) ``` 在上述代码中,我们先定义了一个距离矩阵表示城市之间的距离。然后指定一个起始城市,调用`tsp`函数来求解TSP问题。 该求解算法采用了贪心策略,每次选择当前城市到未访问城市中距离最短的城市作为下一个要访问的城市,直到所有城市都被访问过。 最后,我们输出求解的最优路线和最短距离。 ### 回答3: 以下是一个使用贪心算法求解旅行商问题TSP)的Python代码: ```python import math def tsp(graph, start): # 创建一个列表,用于记录已访问的城市 visited = [False] * len(graph) # 将起始城市标记为已访问 visited[start] = True # 将起始城市添加到路径中 path = [start] # 初始总路径长度为0 total_distance = 0 current_city = start while len(path) < len(graph): next_city = find_nearest_city(graph, current_city, visited) # 将找到的最近城市添加到路径中 path.append(next_city) # 更新总路径长度 total_distance += graph[current_city][next_city] # 将下一个城市标记为已访问 visited[next_city] = True current_city = next_city # 完成回路,将最后一个城市添加到路径中 path.append(start) # 更新总路径长度 total_distance += graph[current_city][start] return path, total_distance def find_nearest_city(graph, current_city, visited): # 初始化最小距离为正无穷大 min_distance = math.inf next_city = None # 遍历所有城市 for city in range(len(graph)): # 如果城市未被访问且距离更短,则更新最小距离和下一个城市 if not visited[city] and graph[current_city][city] < min_distance: min_distance = graph[current_city][city] next_city = city return next_city # 示例使用 if __name__ == "__main__": # 示例图的邻接矩阵形式 graph = [ [0, 2, 9, 10], [1, 0, 6, 4], [15, 7, 0, 8], [6, 3, 12, 0] ] start_city = 0 path, total_distance = tsp(graph, start_city) print("最短路径为:", path) print("最短路径长度为:", total_distance) ``` 上述代码通过遍历寻找距离最近的城市,并使用贪心策略不断添加到路径中,最终得到一个近似最优解。请注意,上述代码仅适用于小规问题。对于更大的问题,需要使用其他更高效的算法如动态规划或遗传算法等求解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

u小鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值