TSP问题-简介与部分解法

TSP问题

问题描述

在一个具有n个城市的完全图中,旅行者希望进行一次巡回旅行,或经历一次哈密顿
回路,可以恰好访问每一个城市一次,并且最终回到出发城市。而这次巡回旅行的
总费用为访问各个城市费用的总和,故旅行者同时希望整个行程的费用是最低的,
求这个路线的排列策略?

TSP问题可以抽象为

在一个带权重的完全无向图中,找到一个权值总和最小的哈密顿回路

显然,TSP问题的组合解有N!种组合,随着城市数量N的规模增加,组合数将呈指数级别递增,故使用穷举法将会面临组合爆炸问题,因此TSP属于NP完全问题

解决方案

常用的方法包括:分枝定界法、线性规划法、动态规划法等。但是,随着问题规模的增大,精确算法将变得无能为力,因此,在后来的研究中,国内外学者重点使用近似算法或启发式算法,主要有遗传算法、模拟退火法、蚁群算法、禁忌搜索算法、贪婪算法和神经网络等。

提示:以下代码能稳定得出结果的是动态规划,如果您想要了解能够得到最小代价路线的算法,建议您直接查看动态规划。

近似算法

参考

TSP问题—近似算法 - 简书

费用函数也叫代价函数,指的是两个城市之间的费用指数或者代价程度的量化。在
大多数的实际情况中,从一个地方u直接到另一个地方w,这个走法花费的代价总是
最小的,而如果从u到w需要经过某个中转站v,则这种走法花费的代价却不可能比直
接到达的走法花费的代价更小

数学语言描述:
C ( u , w ) ≤ C ( u , v ) + C ( v , w ) C(u, w) \leq C(u, v) + C(v, w) C(u,w)C(u,v)+C(v,w)

c是费用函数,这个方程说明了,直接从u->w花费的代价,要比从u->v->w花费的代价要小,我们称这个费用函数满足三角不等式

方案步骤(Prim)

(1) 选择G的任意一个顶点r作为根节点(出发点/终点)
(2) 用Prim算法找出G的一颗最小生成树T
(3) 前序遍历访问树T,得到顺序组成的顶点表L
(4) 将r加入顶点表L的末尾,按L中顶点的次序组成哈密顿回路H

当费用函数满足三角不等式时,上述近似算法找出的哈密顿回路产生的总费用,不会超过最优回路的2倍

性质描述

存储结构:
矩阵中的每一行代表G中每一个的顶点到其余各个顶点的费用(欧式距离),如果出现到达不了或者自身到达自身的情况,我们用无穷大inf来填充表示不可达(案例以邻接矩阵作为存储结构),后面的测试代码,部分用的是0表示不可达

Prim算法:
在了解Prim算法之前,首先要知道什么是生成树,什么是最小生成树
连通图:

在图中任一顶点都能到另一顶点至少存在一条通路

生成树:

指满足以下条件的连通图:
1. 包含图中所有的顶点
2. 任意顶点之间有且仅有一条通路

对一个连通图而言,它的生成树可能有很多种。假如此时的图中每一条边都代表了不同的权值,那么对不同的生成树,其对应的权值可能不同。

最小生成树:

在联通图中找到对应权值最小的生成树

Prim算法:

1. 将联通图的顶点分为两类:A类、B类。初始状态下,所有顶点位于B类,A类为空
2. 选择任一顶点,将其从B类移动到A类
3. 从B类的所有顶点出发,找到一条连接着A类中的某个顶点且权值最小的边,将此边连接着A类中的顶点从B类移动到A类
4. 重复第3步,直到B类中的所有顶点全部移动到A类,恰好可以找到N-1条边,结果从结构上是一颗二叉树(每个点度数最多只有2)。

提示

代码中添加了一些没有必要的功能,请读者查看时优先或者只看代码上方提示的主要算法部分,以节省您的时间,祝您生活愉快!


测试1-近似算法(Prim)

算法思想

  1. 采用Prim算法扫面原图创建生成树
  2. 深度遍历生成树得到一条路径
    1. 再将最后一个节点与起点相连,就得到一条回路,但并不是最优解。

得到的结果近似最优解,因此这种解法称为近似解法。

输入样例

第一行输入顶点数points,第二行输入边数edges,随后edges行,每行输入u v w (u,v表示顶点,w表示uv的路径权值)

Please enter the vertex order of the Graphs:
The Points: 6
The Edges: 15
Please enter the adjacency matrix of Graph:
0 1 5
0 2 1
0 3 6
1 2 4
2 3 5
2 4 6
2 5 4
1 5 2
3 4 3
4 5 6
0 4 7
0 5 7
1 3 7
1 4 7
3 5 7

输出结果

输出原连通图的邻接矩阵,生成树的邻接矩阵,从起点到终点的路径,路径总代价

Print the adjacency matrix:
0 5 1 6 7 7
5 0 4 7 7 2
1 4 0 5 6 4
6 7 5 0 3 7
7 7 6 3 0 6
7 2 4 7 6 0
Print the adjacency matrix:
0 0 1 0 0 0
0 0 4 0 0 2
1 4 0 5 0 0
0 0 5 0 3 0
0 0 0 3 0 0
0 2 0 0 0 0
The RePath_N :
[(0)]---1-->[(2)]---5-->[(3)]---3-->[(4)]---7-->[(1)]---2-->[(5)]---7-->[(0)]
Total Cost: 25

测试代码

算法核心位置:Graph getShortPath_ByPrim(int u_id)vector getPath_ByDFS(int u_id)

#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
#include <cstring>
using namespace std;

const int inf = INT_MAX;

class Graph
{
   
private:
    int points;
    int edges;
    vector<vector<int>> Matrix; // 邻接矩阵

public:
    // 邻接矩阵、顶点度数初始化
    Graph(int points, int edges = 0);
    // 设置顶点数
    void setPoints(int points);
    // 获取顶点数
    int getPoints();
    // 设置边数
    void setEdges(int edges);
    // 获得边数
    int getEdges();
    // 打印邻接矩阵
    void Print_Matirx();
    // 输入邻接矩阵
    void setMatrix();
    // 往图中加入新的边
    void add_edge(int u, int v, int w);
    // 生成最小生成树,返回生成树的邻接矩阵(Prim算法)
    Graph getShortPath_ByPrim(int u_id);
    // 深度遍历生成树
    vector<int> getPath_ByDFS(int u_id);
    // 打印回路
    void Print_RePath_N(int u_id, vector<int> &res);
};
Graph::Graph(int points, int edges)
{
   
    // memset(Matrix, 0, sizeof(Matrix)); // 来自<cstring>
    this->points = points;
    this->edges = edges;
    vector<vector<int>> tmp(points, vector<int>(points, inf));
    Matrix = tmp;
}

void Graph::setPoints(int points)
{
   
    this->points = points;
}

int Graph::getPoints()
{
   
    return points;
}

void Graph::setEdges(int edges)
{
   
    this->edges = edges;
}

int Graph::getEdges()
{
   
    return edges;
}

void Graph::Print_Matirx()
{
   
    cout << "Print the adjacency matrix: " << endl;
    int i, j;
    for (i = 0; i < points; i++)
    {
   
        for (j = 0; j < points - 1; j++)
        {
   
            if (Matrix[i][j] == inf)
                cout << 0 << " ";
            else
                cout << Matrix[i][j] << " ";
        }
        if (Matrix[i][j] == inf)
            cout << 0 << endl;
        else
            cout << Matrix[i][j] << endl;
    }
}

void Graph::setMatrix()
{
   
    int u, v, weight;
    for (int i = 0; i < this->edges; i++)
    {
   
        cin >> u >> v >> weight;
        Matrix[u][v] = weight;
        Matrix[v][u] = weight;
    }
}

void Graph::add_edge(int u, int v, int w)
{
   
    Matrix[u][v] = w;
    Matrix[v][u] = w;
}

Graph Graph::getShortPath_ByPrim(int u_id)
{
   
    Graph res(points);
    bool visit[points];               // 默认为false
    vector<int> lowcost(points, inf); // 存放点与点间最小权值,会不断更新
    vector<int> parent(points, -1);   // 记录路径(当前节点前一个节点的坐标)

    int edges = 0;     // 统计生成树的边数
    lowcost[u_id] = 0; // 默认认为从无到第一个顶点u_id的距离为0

    for (int i = 0; i < points; i++)
    {
   
        int minlen = inf;
        int u = -1;
        for (int j = 0; j < points; j++)
        {
   
            if (lowcost[j] < minlen && !visit[j])
            {
   
                minlen = lowcost[j];
                u = j;
            }
        }
        if (u == -1)
            break;
        visit[u] = true;
        int v = parent[u];
        if (v != -1)
        {
   
            res.add_edge(u, v, minlen);
            edges++;
        }
        for (int j = 0; j < points; j++)
        {
   
         
  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TSP问题是指在给定的一些城市之间,求解访问每个城市一次并回到起始城市的最短路径。其中,TSP问题是一个NP难问题,因此需要使用一些高效的算法来解决。模拟退火算法是其中一种常用的算法之一,可以用来求解TSP问题。以下是一个使用Python实现的TSP问题的模拟退火算法解法: ```python # -*- coding: utf-8 -*- import numpy as np import math import random # 计算两个城市之间的距离 def distance(city1, city2): return math.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2) # 计算路径长度 def path_length(path, cities): length = 0 for i in range(len(path) - 1): length += distance(cities[path[i]], cities[path[i+1]]) length += distance(cities[path[-1]], cities[path[0]]) return length # 模拟退火算法求解TSP问题 def tsp_simulated_annealing(cities, T=10000, alpha=0.99, stopping_T=1e-8, stopping_iter=100000): # 初始化路径 path = list(range(len(cities))) # 初始化温度 temperature = T # 记录最优路径和长度 best_path, best_length = path, path_length(path, cities) # 迭代次数 iter_num = 0 # 迭代 while temperature > stopping_T and iter_num < stopping_iter: # 生成新路径 new_path = path.copy() # 随机交换两个城市的位置 rand1 = random.randint(0, len(cities) - 1) rand2 = random.randint(0, len(cities) - 1) while rand1 == rand2: rand2 = random.randint(0, len(cities) - 1) new_path[rand1], new_path[rand2] = new_path[rand2], new_path[rand1] # 计算路径长度差 delta = path_length(new_path, cities) - path_length(path, cities) # 判断是否接受新路径 if delta < 0 or math.exp(-delta / temperature) > random.random(): path = new_path # 更新最优路径 if path_length(path, cities) < best_length: best_path, best_length = path.copy(), path_length(path, cities) # 降温 temperature *= alpha # 迭代次数加1 iter_num += 1 return best_path, best_length # 测试 if __name__ == '__main__': # 随机生成10个城市 cities = np.random.rand(10, 2) # 求解TSP问题 best_path, best_length = tsp_simulated_annealing(cities) print('最优路径:', best_path) print('最短路径长度:', best_length) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值