Problema del vendedor ambulante: Python, C++ Algoritmo
โก Resumen inteligente
El problema del viajante es una tarea de optimizaciรณn clรกsica de complejidad NP-difรญcil que consiste en encontrar el recorrido mรกs corto que visite cada ciudad exactamente una vez y regrese al punto de origen, utilizando datos de distancia proporcionados a travรฉs de un grafo.

ยฟQuรฉ es el problema del viajante (TSP)?
El problema del viajante (TSP, por sus siglas en inglรฉs) es un problema clรกsico de optimizaciรณn combinatoria en la informรกtica teรณrica. Dado un grafo de ciudades, el TSP busca el camino mรกs corto que visite cada nodo exactamente una vez y regrese a la ciudad de origen.
El enunciado del problema proporciona una lista de ciudades junto con las distancias entre cada par de ciudades.
Objetivo: Empieza en la ciudad de origen, visita todas las demรกs ciudades exactamente una vez y regresa a la ciudad de partida. El objetivo es encontrar la ruta de ida y vuelta mรกs corta posible.
Ejemplo de TSP
Consideremos el grรกfico que aparece a continuaciรณn, donde 1, 2, 3 y 4 representan las ciudades, y el peso de cada arista representa la distancia entre esas ciudades.
El objetivo es encontrar la ruta mรกs corta posible que comience en la ciudad de origen, visite todas las demรกs ciudades exactamente una vez y regrese a la ciudad de origen.
Para el grรกfico anterior, la ruta รณptima es 1-2-4-3-1. El costo del tour mรกs corto es 10 + 25 + 30 + 15 = 80.
Diferentes soluciones al problema del viajante
El problema del viajante se clasifica como NP-difรญcil porque no existe ningรบn algoritmo conocido de tiempo polinomial que lo resuelva exactamente. La complejidad aumenta exponencialmente con el nรบmero de ciudades.
Existen mรบltiples formas de abordar el problema del viajante. Los enfoques mรกs comunes son:
Enfoque de fuerza bruta: El mรฉtodo ingenuo calcula todos los recorridos posibles y los compara. El nรบmero de recorridos en un grafo con n ciudades es n!, lo que hace que la fuerza bruta sea computacionalmente muy costosa para cualquier cosa que supere las diez ciudades aproximadamente.
Mรฉtodo de ramificaciรณn y acotaciรณn: El problema se divide en subproblemas, y las soluciones de estos subproblemas se combinan para formar una soluciรณn รณptima. Una poda eficaz descarta los recorridos parciales que no pueden superar el mejor coste actual.
Este tutorial demuestra la enfoque de programaciรณn dinรกmica, que es la versiรณn memorizada del mรฉtodo de ramificaciรณn y acotaciรณn y coincide con el algoritmo de Bellman-Held-Karp.
Programaciรณn dinรกmica: Este es un mรฉtodo exacto que busca la soluciรณn รณptima mediante la reutilizaciรณn de la superposiciรณn.ping resultados del subproblema. Es mรกs lento que el casi รณptimo. mรฉtodos codiciosospero siempre ofrece un recorrido รณptimo a nivel global.
La complejidad computacional de este enfoque es O(Nยฒ ร 2^N), tema que abordaremos mรกs adelante en el artรญculo.
Mรฉtodo del vecino mรกs cercano: Un enfoque heurรญstico voraz que siempre salta a la ciudad no visitada mรกs cercana. Es mucho mรกs econรณmico que la programaciรณn dinรกmica, pero no garantiza un recorrido รณptimo, por lo que se utiliza para soluciones casi รณptimas cuando la velocidad es mรกs importante que la obtenciรณn de mรญnimos exactos.
Algoritmo para el problema del viajante
Utilizamos el enfoque de programaciรณn dinรกmica para resolver el problema del viajante. Antes de comenzar con el algoritmo, aclaremos algunos tรฉrminos:
- Un grรกfico
G = (V, E)es un conjunto de vรฉrtices y aristas. Ves el conjunto de vรฉrtices.Ees el conjunto de aristas.- Los vรฉrtices estรกn conectados a travรฉs de aristas.
Dist(i, j)denota la distancia no negativa entre los vรฉrtices i y j.
Supongamos que S es un subconjunto de ciudades extraรญdas de {1, 2, 3, โฆ, n}, donde i y j son dos ciudades de ese subconjunto. Entonces cost(i, S, j) es la longitud del camino mรกs corto que comienza en i, visita cada ciudad en S exactamente una vez y termina en j.
Por ejemplo, cost(1, {2, 3, 4}, 1) denota el camino mรกs corto donde:
- La ciudad inicial es 1.
- Las ciudades 2, 3 y 4 se visitan solo una vez
- El punto final es 1.
La recurrencia de la programaciรณn dinรกmica es:
- Establecer
cost(i, {}, i) = 0, lo que significa que comenzamos y terminamos en i con costo cero. - Cuando
|S| > 1, definircost(i, S, 1) = โpor lai โ 1, porque el coste real del tour aรบn se desconoce. - Comenzando en la ciudad 1, elige la siguiente ciudad de modo que
cost(i, S, j) = min [ cost(i, S โ {i}, j) + dist(i, j) ]por lai โ Syi โ j.
Para el grรกfico anterior, la matriz de adyacencia es la siguiente:
| distancia(i, j) | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 1 | 0 | 10 | 15 | 20 |
| 2 | 10 | 0 | 35 | 25 |
| 3 | 15 | 35 | 0 | 30 |
| 4 | 20 | 25 | 30 | 0 |
Asรญ es como procede el algoritmo:
Paso 1) El viaje comienza en la ciudad 1, visita todas las demรกs ciudades una vez y regresa a la ciudad 1.
Paso 2) S es un subconjunto de ciudades. Para cada |S| > 1, inicializar cost(i, S, 1) = โ. aquรญ cost(i, S, j) denota un recorrido que comienza en i, visita las ciudades en S una vez y llega a j. Comenzamos desde el infinito porque la distancia es desconocida en este punto. Por lo tanto, los valores son:
cost(2, {3, 4}, 1) = โ significa que comenzamos en la ciudad 2, pasamos por las ciudades 3 y 4, y llegamos a la 1, con un costo desconocido. De manera similar:
cost(3, {2, 4}, 1) = โ
cost(4, {2, 3}, 1) = โ
Paso 3) Para cada subconjunto de S, calcule:
cost(i, S, j) = min [ cost(i, S โ {i}, j) + dist(i, j) ], donde j โ S y i โ j.
Ese es el recorrido de costo mรญnimo que comienza en i, visita el subconjunto de ciudades una vez y regresa a j. Debido a que el recorrido comienza en la ciudad 1, el costo รณptimo es cost(1, {other cities}, 1).
Trabajar la recurrencia paso a paso
Ahora S = {1, 2, 3, 4}. Hay cuatro elementos, por lo que el nรบmero de subconjuntos es 2^4 = 16Esos subconjuntos son:
1) |S| = 0: {ฮฆ}
2) |S| = 1: {{1}, {2}, {3}, {4}}
3) |S| = 2: {{1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}}
4) |S| = 3: {{1, 2, 3}, {1, 2, 4}, {2, 3, 4}, {1, 3, 4}}
5) |S| = 4: {{1, 2, 3, 4}}
Dado que el recorrido comienza en la ciudad 1, podemos descartar cualquier subconjunto que contenga la ciudad 1 al calcular los costos intermedios.
El cรกlculo del algoritmo se desarrolla de la siguiente manera:
1) |S| =ฮฆ:
- costo(2, ฮฆ, 1) = dist(2, 1) = 10
- costo(3, ฮฆ, 1) = dist(3, 1) = 15
- costo(4, ฮฆ, 1) = dist(4, 1) = 20
2) |S| = 1:
- costo(2, {3}, 1) = dist(2, 3) + costo(3, ฮฆ, 1) = 35 + 15 = 50
- costo(2, {4}, 1) = dist(2, 4) + costo(4, ฮฆ, 1) = 25 + 20 = 45
- costo(3, {2}, 1) = dist(3, 2) + costo(2, ฮฆ, 1) = 35 + 10 = 45
- costo(3, {4}, 1) = dist(3, 4) + costo(4, ฮฆ, 1) = 30 + 20 = 50
- costo(4, {2}, 1) = dist(4, 2) + costo(2, ฮฆ, 1) = 25 + 10 = 35
- costo(4, {3}, 1) = dist(4, 3) + costo(3, ฮฆ, 1) = 30 + 15 = 45
3) |S| = 2:
- costo(2, {3, 4}, 1) = min [ dist(2, 3) + costo(3, {4}, 1) = 35 + 50 = 85, dist(2, 4) + costo(4, {3}, 1) = 25 + 45 = 70 ] = 70
- costo(3, {2, 4}, 1) = min [ dist(3, 2) + costo(2, {4}, 1) = 35 + 45 = 80, dist(3, 4) + costo(4, {2}, 1) = 30 + 35 = 65 ] = 65
- costo(4, {2, 3}, 1) = min [ dist(4, 2) + costo(2, {3}, 1) = 25 + 50 = 75, dist(4, 3) + costo(3, {2}, 1) = 30 + 45 = 75 ] = 75
4) |S| = 3:
- costo(1, {2, 3, 4}, 1) = min [ dist(1, 2) + costo(2, {3, 4}, 1) = 10 + 70 = 80, dist(1, 3) + costo(3, {2, 4}, 1) = 15 + 65 = 80, dist(1, 4) + costo(4, {2, 3}, 1) = 20 + 75 = 95 ] = 80
Por lo tanto, la soluciรณn รณptima es 1-2-4-3-1.
Pseudocรณdigo
Algorithm: Traveling-Salesman-Problem
Cost (1, {}, 1) = 0
for s = 2 to n do
for all subsets S belongs to {1, 2, 3, ..., n} of size s
Cost (s, S, 1) = Infinity
for all i in S and i != 1
Cost (i, S, j) = min {Cost (i, S - {i}, j) + dist(i, j) for j in S and i != j}
Return min(i) Cost (i, {1, 2, 3, ..., n}, j) + d(j, i)
Implantaciรณn en C/C++
Aquรญ estรก la implementaciรณn en C++La versiรณn que aparece a continuaciรณn corrige el error inicial del cรณdigo fuente. return error, que se producรญa despuรฉs de la primera permutaciรณn en lugar de enumerar todos los recorridos.
#include <bits/stdc++.h> using namespace std; #define V 4 #define MAX 1000000 int tsp(int graph[][V], int s) { vector<int> vertex; for (int i = 0; i < V; i++) if (i != s) vertex.push_back(i); int min_cost = MAX; do { int current_cost = 0; int j = s; for (int i = 0; i < vertex.size(); i++) { current_cost += graph[j][vertex[i]]; j = vertex[i]; } current_cost += graph[j][s]; min_cost = min(min_cost, current_cost); } while (next_permutation(vertex.begin(), vertex.end())); return min_cost; } int main() { int graph[][V] = { { 0, 10, 15, 20 }, { 10, 0, 35, 25 }, { 15, 35, 0, 30 }, { 20, 25, 30, 0 } }; int s = 0; cout << tsp(graph, s) << endl; return 0; }
Salida:
80
Implementaciรณn en Python
El Python La implementaciรณn refleja la C++ versiรณn. Corrige la fuente from itertools, import error tipogrรกfico de coma, el mal colocado return dentro del bucle interior y la hendidura suelta en s = 0.
from sys import maxsize from itertools import permutations V = 4 def tsp(graph, s): vertex = [] for i in range(V): if i != s: vertex.append(i) min_cost = maxsize for perm in permutations(vertex): current_cost = 0 k = s for j in perm: current_cost += graph[k][j] k = j current_cost += graph[k][s] min_cost = min(min_cost, current_cost) return min_cost graph = [[0, 10, 15, 20], [10, 0, 35, 25], [15, 35, 0, 30], [20, 25, 30, 0]] s = 0 print(tsp(graph, s))
Salida:
80
Soluciones acadรฉmicas para TSP
Los informรกticos han dedicado dรฉcadas a buscar algoritmos mejorados de tiempo polinomial para el problema del viajante. Hasta ahora, el problema del viajante sigue siendo NP-difรญcil.
Varias tรฉcnicas publicadas reducen la complejidad prรกctica para familias especรญficas de instancias del problema del viajante:
- El TSP simรฉtrico clรกsico se resuelve mediante el Mรฉtodo del sufijo cero.
- El Algoritmo de optimizaciรณn basado en biogeografรญa Utiliza estrategias de migraciรณn para resolver problemas de optimizaciรณn que se corresponden con el problema del viajante.
- El Algoritmo evolutivo multiobjetivo Estรก diseรฑado para el problema del viajante multiobjetivo y se basa en NSGA-II.
- El Sistema Multi-Agente Este enfoque resuelve el problema del viajante para N ciudades con recursos computacionales fijos.
- El Heurรญstica de Lin-Kernighan y su sucesor LKH Ofrecer recorridos con una precisiรณn de entre el 2 % y el 3 % respecto al รณptimo en casos con millones de ciudades.
- Concord Utiliza planos de corte y el mรฉtodo de ramificaciรณn y corte para calcular รณptimos exactos para instancias de referencia con decenas de miles de ciudades.
Aplicaciรณn del problema del viajante
El problema del viajante se presenta en el mundo real tanto en su forma pura como modificada. Algunas de sus principales aplicaciones son:
- Planificaciรณn, logรญstica y fabricaciรณn de microchips: Los problemas de inserciรณn de chips en la industria de los microchips se modelan como variantes del problema del viajante para minimizar el tiempo de desplazamiento del brazo robรณtico.
- Secuencia ADN: En la secuenciaciรณn de ADN se utiliza un TSP modificado, donde las ciudades representan fragmentos de ADN y las distancias representan la similitud entre los fragmentos.
- Astronomรญa: Los astrรณnomos utilizan TSP para minimizar el tiempo que se tarda en mover los telescopios entre los objetivos de observaciรณn.
- Control รณptimo: Las formulaciones del problema del viajante modelan problemas de control รณptimo en los que se deben respetar mรบltiples restricciones al tiempo que se minimiza el coste de recorrido.
- Entrega de รบltima milla: AmazonLas aplicaciones de reparto de comida, como UPS, resuelven variantes dinรกmicas del problema del viajante para secuenciar las paradas de los conductores.
- Picking en almacรฉn: Los operarios, tanto robรณticos como humanos, siguen rutas optimizadas mediante el algoritmo TSP que reducen el tiempo de desplazamiento dentro de los centros de distribuciรณn.
Anรกlisis de complejidad de TSP
- Complejidad del tiempo: El enfoque de programaciรณn dinรกmica de Held-Karp resuelve 2N subconjuntos para cada nodo inicial, dando
N ร 2^Nsubproblemas. Cada subproblema requiere un tiempo lineal para combinarse. Si el nodo de origen no estรก especificado, se requiere un bucle externo sobre N nodos. La complejidad temporal total esO(Nยฒ ร 2^N). - Complejidad espacial: La tabla DP almacena
C(S, i)para cada subconjunto S del conjunto de vรฉrtices. Hay 2N subconjuntos por nodo, por lo que la complejidad espacial esO(N ร 2^N), que a menudo se escribe comoO(2^N)cuando N se considera fijo.
A continuaciรณn, aprenda sobre el Algoritmo del tamiz de Eratรณstenes.




