- 数据结构与算法
- DSA - 首页
- DSA - 概述
- DSA - 环境设置
- DSA - 算法基础
- DSA - 渐进分析
- 数据结构
- DSA - 数据结构基础
- DSA - 数据结构和类型
- DSA - 数组数据结构
- 链表
- DSA - 链表数据结构
- DSA - 双向链表数据结构
- DSA - 循环链表数据结构
- 栈与队列
- DSA - 栈数据结构
- DSA - 表达式解析
- DSA - 队列数据结构
- 搜索算法
- DSA - 搜索算法
- DSA - 线性搜索算法
- DSA - 二分搜索算法
- DSA - 插值搜索
- DSA - 跳跃搜索算法
- DSA - 指数搜索
- DSA - 斐波那契搜索
- DSA - 子列表搜索
- DSA - 哈希表
- 排序算法
- DSA - 排序算法
- DSA - 冒泡排序算法
- DSA - 插入排序算法
- DSA - 选择排序算法
- DSA - 归并排序算法
- DSA - 希尔排序算法
- DSA - 堆排序
- DSA - 桶排序算法
- DSA - 计数排序算法
- DSA - 基数排序算法
- DSA - 快速排序算法
- 图数据结构
- DSA - 图数据结构
- DSA - 深度优先遍历
- DSA - 广度优先遍历
- DSA - 生成树
- 树数据结构
- DSA - 树数据结构
- DSA - 树的遍历
- DSA - 二叉搜索树
- DSA - AVL树
- DSA - 红黑树
- DSA - B树
- DSA - B+树
- DSA - 伸展树
- DSA - 字典树
- DSA - 堆数据结构
- 递归
- DSA - 递归算法
- DSA - 使用递归的汉诺塔问题
- DSA - 使用递归的斐波那契数列
- 分治法
- DSA - 分治法
- DSA - 最大-最小问题
- DSA - Strassen矩阵乘法
- DSA - Karatsuba算法
- 贪心算法
- DSA - 贪心算法
- DSA - 旅行商问题(贪心算法)
- DSA - Prim最小生成树
- DSA - Kruskal最小生成树
- DSA - Dijkstra最短路径算法
- DSA - 地图着色算法
- DSA - 分数背包问题
- DSA - 带截止日期的作业排序
- DSA - 最优合并模式算法
- 动态规划
- DSA - 动态规划
- DSA - 矩阵链乘法
- DSA - Floyd-Warshall算法
- DSA - 0-1背包问题
- DSA - 最长公共子序列算法
- DSA - 旅行商问题(动态规划方法)
- 近似算法
- DSA - 近似算法
- DSA - 顶点覆盖算法
- DSA - 集合覆盖问题
- DSA - 旅行商问题(近似算法)
- 随机算法
- DSA - 随机算法
- DSA - 随机快速排序算法
- DSA - Karger最小割算法
- DSA - Fisher-Yates洗牌算法
- DSA 有用资源
- DSA - 问答
- DSA - 快速指南
- DSA - 有用资源
- DSA - 讨论
使用近似算法的旅行商问题
我们已经讨论了使用贪心和动态规划方法解决旅行商问题,并且已经确定在多项式时间内无法找到旅行商问题的完美最优解。
因此,期望近似解能够找到此 NP-Hard 问题的近似最优解。但是,只有当问题中的成本函数(定义为两个绘图点之间的距离)满足三角不等式时,才会设计近似算法。
只有当成本函数 c 对于三角形 u、v 和 w 的所有顶点都满足以下等式时,才满足三角不等式
c(u, w)≤ c(u, v)+c(v, w)
在许多应用中,它通常会自动满足。
旅行商近似算法
旅行商近似算法需要执行一些先决条件算法,以便我们能够获得近似最优解。让我们简要了解一下这些先决条件算法:
最小生成树 - 最小生成树是一种树形数据结构,它包含主图的所有顶点,以及连接它们的最小数量的边。在这种情况下,我们应用 Prim 算法来生成最小生成树。
先序遍历 - 先序遍历是在树形数据结构上进行的,其中一个指针以 [根 - 左孩子 - 右孩子] 的顺序遍历树的所有节点。
算法
步骤 1 - 随机选择给定图中的任意顶点作为起点和终点。
步骤 2 - 使用 Prim 算法构建以所选顶点为根的图的最小生成树。
步骤 3 - 一旦构建了生成树,就在上一步获得的最小生成树上执行先序遍历。
步骤 4 - 获得的先序解是旅行商的哈密顿路径。
伪代码
APPROX_TSP(G, c) r <- root node of the minimum spanning tree T <- MST_Prim(G, c, r) visited = {ф} for i in range V: H <- Preorder_Traversal(G) visited = {H}
分析
如果满足三角不等式,则旅行商问题的近似算法是 2-近似算法。
为了证明这一点,我们需要证明问题的近似成本是最佳成本的两倍。以下是一些支持此论断的观察结果:
最小生成树的成本永远不会小于最优哈密顿路径的成本。也就是说,c(M) ≤ c(H*)。
完整遍历的成本也是最小生成树成本的两倍。完整遍历定义为按先序遍历最小生成树时所描绘的路径。完整遍历精确地遍历图中的每条边两次。因此,c(W) = 2c(T)
由于先序遍历路径小于完整遍历路径,因此算法的输出始终低于完整遍历的成本。
示例
让我们看一个示例图来可视化此近似算法:
解决方案
从上图中考虑顶点 1 作为旅行商的起点和终点,并从此处开始算法。
步骤 1
从顶点 1 开始算法,从图中构建一个最小生成树。要了解有关构建最小生成树的更多信息,请点击此处。
步骤 2
一旦构建了最小生成树,就将起始顶点视为根节点(即顶点 1),并按先序遍历生成树。
旋转生成树以方便解释,我们得到:
发现树的先序遍历为:1 → 2 → 5 → 6 → 3 → 4
步骤 3
在追踪路径的末尾添加根节点,我们得到1 → 2 → 5 → 6 → 3 → 4 → 1
这是旅行商近似问题的输出哈密顿路径。路径的成本将是最小生成树中所有成本的总和,即55。
实施
以下是上述方法在各种编程语言中的实现:
#include <stdio.h> #include <stdbool.h> #include <limits.h> #define V 6 // Number of vertices in the graph // Function to find the minimum key vertex from the set of vertices not yet included in MST int findMinKey(int key[], bool mstSet[]) { int min = INT_MAX, min_index; for (int v = 0; v < V; v++) { if (mstSet[v] == false && key[v] < min) { min = key[v]; min_index = v; } } return min_index; } // Function to perform Prim's algorithm to find the Minimum Spanning Tree (MST) void primMST(int graph[V][V], int parent[]) { int key[V]; bool mstSet[V]; for (int i = 0; i < V; i++) { key[i] = INT_MAX; mstSet[i] = false; } key[0] = 0; parent[0] = -1; for (int count = 0; count < V - 1; count++) { int u = findMinKey(key, mstSet); mstSet[u] = true; for (int v = 0; v < V; v++) { if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v]) { parent[v] = u; key[v] = graph[u][v]; } } } } // Function to print the preorder traversal of the Minimum Spanning Tree void printPreorderTraversal(int parent[]) { printf("The preorder traversal of the tree is found to be − "); for (int i = 1; i < V; i++) { printf("%d → ", parent[i]); } printf("\n"); } // Main function for the Traveling Salesperson Approximation Algorithm void tspApproximation(int graph[V][V]) { int parent[V]; int root = 0; // Choosing vertex 0 as the starting and ending point // Find the Minimum Spanning Tree using Prim's Algorithm primMST(graph, parent); // Print the preorder traversal of the Minimum Spanning Tree printPreorderTraversal(parent); // Print the Hamiltonian path (preorder traversal with the starting point added at the end) printf("Adding the root node at the end of the traced path "); for (int i = 0; i < V; i++) { printf("%d → ", parent[i]); } printf("%d → %d\n", root, parent[0]); // Calculate and print the cost of the Hamiltonian path int cost = 0; for (int i = 1; i < V; i++) { cost += graph[parent[i]][i]; } // The cost of the path would be the sum of all the costs in the minimum spanning tree. printf("Sum of all the costs in the minimum spanning tree %d.\n", cost); } int main() { // Example graph represented as an adjacency matrix int graph[V][V] = { {0, 3, 1, 6, 0, 0}, {3, 0, 5, 0, 3, 0}, {1, 5, 0, 5, 6, 4}, {6, 0, 5, 0, 0, 2}, {0, 3, 6, 0, 0, 6}, {0, 0, 4, 2, 6, 0} }; tspApproximation(graph); return 0; }
输出
The preorder traversal of the tree is found to be − 0 → 0 → 5 → 1 → 2 → Adding the root node at the end of the traced path -1 → 0 → 0 → 5 → 1 → 2 → 0 → -1 Sum of all the costs in the minimum spanning tree 13.
#include <iostream> #include <limits> #define V 6 // Number of vertices in the graph // Function to find the minimum key vertex from the set of vertices not yet included in MST int findMinKey(int key[], bool mstSet[]) { int min = std::numeric_limits<int>::max(); int min_index; for (int v = 0; v < V; v++) { if (mstSet[v] == false && key[v] < min) { min = key[v]; min_index = v; } } return min_index; } // Function to perform Prim's algorithm to find the Minimum Spanning Tree (MST) void primMST(int graph[V][V], int parent[]) { int key[V]; bool mstSet[V]; for (int i = 0; i < V; i++) { key[i] = std::numeric_limits<int>::max(); mstSet[i] = false; } key[0] = 0; parent[0] = -1; for (int count = 0; count < V - 1; count++) { int u = findMinKey(key, mstSet); mstSet[u] = true; for (int v = 0; v < V; v++) { if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v]) { parent[v] = u; key[v] = graph[u][v]; } } } } // Function to print the preorder traversal of the Minimum Spanning Tree void printPreorderTraversal(int parent[]) { std::cout << "The preorder traversal of the tree is found to be − "; for (int i = 1; i < V; i++) { std::cout << parent[i] << " → "; } std::cout << std::endl; } // Main function for the Traveling Salesperson Approximation Algorithm void tspApproximation(int graph[V][V]) { int parent[V]; int root = 0; // Choosing vertex 0 as the starting and ending point // Find the Minimum Spanning Tree using Prim's Algorithm primMST(graph, parent); // Print the preorder traversal of the Minimum Spanning Tree printPreorderTraversal(parent); // Print the Hamiltonian path (preorder traversal with the starting point added at the end) std::cout << "Adding the root node at the end of the traced path "; for (int i = 0; i < V; i++) { std::cout << parent[i] << " → "; } std::cout << root << " → " << parent[0] << std::endl; // Calculate and print the cost of the Hamiltonian path int cost = 0; for (int i = 1; i < V; i++) { cost += graph[parent[i]][i]; } // The cost of the path would be the sum of all the costs in the minimum spanning tree. std::cout << "Sum of all the costs in the minimum spanning tree: " << cost << "." << std::endl; } int main() { // Example graph represented as an adjacency matrix int graph[V][V] = { {0, 3, 1, 6, 0, 0}, {3, 0, 5, 0, 3, 0}, {1, 5, 0, 5, 6, 4}, {6, 0, 5, 0, 0, 2}, {0, 3, 6, 0, 0, 6}, {0, 0, 4, 2, 6, 0} }; tspApproximation(graph); return 0; }
输出
The preorder traversal of the tree is found to be − 0 → 0 → 5 → 1 → 2 → Adding the root node at the end of the traced path -1 → 0 → 0 → 5 → 1 → 2 → 0 → -1 Sum of all the costs in the minimum spanning tree: 13.
import java.util.Arrays; public class TravelingSalesperson { static final int V = 6; // Number of vertices in the graph // Function to find the minimum key vertex from the set of vertices not yet included in MST static int findMinKey(int key[], boolean mstSet[]) { int min = Integer.MAX_VALUE; int minIndex = -1; for (int v = 0; v < V; v++) { if (!mstSet[v] && key[v] < min) { min = key[v]; minIndex = v; } } return minIndex; } // Function to perform Prim's algorithm to find the Minimum Spanning Tree (MST) static void primMST(int graph[][], int parent[]) { int key[] = new int[V]; boolean mstSet[] = new boolean[V]; Arrays.fill(key, Integer.MAX_VALUE); Arrays.fill(mstSet, false); key[0] = 0; parent[0] = -1; for (int count = 0; count < V - 1; count++) { int u = findMinKey(key, mstSet); mstSet[u] = true; for (int v = 0; v < V; v++) { if (graph[u][v] != 0 && !mstSet[v] && graph[u][v] < key[v]) { parent[v] = u; key[v] = graph[u][v]; } } } } // Function to print the preorder traversal of the Minimum Spanning Tree static void printPreorderTraversal(int parent[]) { System.out.print("The preorder traversal of the tree is found to be "); for (int i = 1; i < V; i++) { System.out.print(parent[i] + " -> "); } System.out.println(); } // Main function for the Traveling Salesperson Approximation Algorithm static void tspApproximation(int graph[][]) { int parent[] = new int[V]; int root = 0; // Choosing vertex 0 as the starting and ending point // Find the Minimum Spanning Tree using Prim's Algorithm primMST(graph, parent); // Print the preorder traversal of the Minimum Spanning Tree printPreorderTraversal(parent); // Print the Hamiltonian path (preorder traversal with the starting point added at the end) System.out.print("Adding the root node at the end of the traced path "); for (int i = 0; i < V; i++) { System.out.print(parent[i] + " -> "); } System.out.println(root + " " + parent[0]); // Calculate and print the cost of the Hamiltonian path int cost = 0; for (int i = 1; i < V; i++) { cost += graph[parent[i]][i]; } // The cost of the path would be the sum of all the costs in the minimum spanning tree. System.out.println("Sum of all the costs in the minimum spanning tree: " + cost); } public static void main(String[] args) { // Example graph represented as an adjacency matrix int graph[][] = { {0, 3, 1, 6, 0, 0}, {3, 0, 5, 0, 3, 0}, {1, 5, 0, 5, 6, 4}, {6, 0, 5, 0, 0, 2}, {0, 3, 6, 0, 0, 6}, {0, 0, 4, 2, 6, 0} }; tspApproximation(graph); } }
输出
The preorder traversal of the tree is found to be 0 -> 0 -> 5 -> 1 -> 2 -> Adding the root node at the end of the traced path -1 -> 0 -> 0 -> 5 -> 1 -> 2 -> 0 -1 Sum of all the costs in the minimum spanning tree: 13
import sys V = 6 # Number of vertices in the graph # Function to find the minimum key vertex from the set of vertices not yet included in MST def findMinKey(key, mstSet): min_val = sys.maxsize min_index = -1 for v in range(V): if not mstSet[v] and key[v] < min_val: min_val = key[v] min_index = v return min_index # Function to perform Prim's algorithm to find the Minimum Spanning Tree (MST) def primMST(graph, parent): key = [sys.maxsize] * V mstSet = [False] * V key[0] = 0 parent[0] = -1 for _ in range(V - 1): u = findMinKey(key, mstSet) mstSet[u] = True for v in range(V): if graph[u][v] and not mstSet[v] and graph[u][v] < key[v]: parent[v] = u key[v] = graph[u][v] # Function to print the preorder traversal of the Minimum Spanning Tree def printPreorderTraversal(parent): print("The preorder traversal of the tree is found to be − ", end="") for i in range(1, V): print(parent[i], " → ", end="") print() # Main function for the Traveling Salesperson Approximation Algorithm def tspApproximation(graph): parent = [0] * V root = 0 # Choosing vertex 0 as the starting and ending point # Find the Minimum Spanning Tree using Prim's Algorithm primMST(graph, parent) # Print the preorder traversal of the Minimum Spanning Tree printPreorderTraversal(parent) # Print the Hamiltonian path (preorder traversal with the starting point added at the end) print("Adding the root node at the end of the traced path ", end="") for i in range(V): print(parent[i], " → ", end="") print(root, " → ", parent[0]) # Calculate and print the cost of the Hamiltonian path cost = 0 for i in range(1, V): cost += graph[parent[i]][i] # The cost of the path would be the sum of all the costs in the minimum spanning tree. print("Sum of all the costs in the minimum spanning tree:", cost) if __name__ == "__main__": # Example graph represented as an adjacency matrix graph = [ [0, 3, 1, 6, 0, 0], [3, 0, 5, 0, 3, 0], [1, 5, 0, 5, 6, 4], [6, 0, 5, 0, 0, 2], [0, 3, 6, 0, 0, 6], [0, 0, 4, 2, 6, 0] ] tspApproximation(graph)
输出
The preorder traversal of the tree is found to be − 0 → 0 → 5 → 1 → 2 → Adding the root node at the end of the traced path -1 → 0 → 0 → 5 → 1 → 2 → 0 → -1 Sum of all the costs in the minimum spanning tree: 13