Problema do caixeiro viajante: Python, C++ Algoritmo
Qual รฉ o problema do caixeiro viajante (TSP)?
O Problema do Caixeiro Viajante (TSP) รฉ um problema combinatรณrio clรกssico da ciรชncia da computaรงรฃo teรณrica. O problema pede para encontrar o caminho mais curto em um grafo com a condiรงรฃo de visitar todos os nรณs apenas uma vez e retornar ร cidade de origem.
A definiรงรฃo do problema fornece uma lista de cidades junto com as distรขncias entre cada cidade.
Objetivo: Para comeรงar na cidade de origem, visite outras cidades apenas uma vez e retorne ร cidade original novamente. Nosso objetivo รฉ encontrar o caminho mais curto possรญvel para completar a rota de ida e volta.
Exemplo de TSP
Aqui รฉ fornecido um grรกfico onde 1, 2, 3 e 4 representam as cidades, e o peso associado a cada aresta representa a distรขncia entre essas cidades.
O objetivo รฉ encontrar o caminho mais curto possรญvel para o passeio que comeรงa na cidade de origem, percorre o grafo visitando apenas uma vez as outras cidades ou nรณs e retorna ร cidade de origem.
Para o grรกfico acima, a rota ideal รฉ seguir o caminho de custo mรญnimo: 1-2-4-3-1. E esse caminho mais curto custaria 10+25+30+15 =80
Diferentes soluรงรตes para o problema do caixeiro viajante
O Problema do Caixeiro Viajante (TSP) รฉ classificado como um problema NP-difรญcil por nรฃo possuir algoritmo de tempo polinomial. A complexidade aumenta exponencialmente com o aumento do nรบmero de cidades.
Existem vรกrias maneiras de resolver o problema do caixeiro viajante (tsp). Algumas soluรงรตes populares sรฃo:
A abordagem da forรงa bruta รฉ o mรฉtodo ingรชnuo para resolver problemas do caixeiro viajante. Nesta abordagem, primeiro calculamos todos os caminhos possรญveis e depois os comparamos. O nรบmero de caminhos em um grafo que consiste em n cidades รฉ n! ร computacionalmente muito caro resolver o problema do caixeiro viajante nesta abordagem de forรงa bruta.
O mรฉtodo branch-and-bound: O problema รฉ dividido em subproblemas nesta abordagem. A soluรงรฃo desses subproblemas individuais forneceria uma soluรงรฃo รณtima.
Este tutorial demonstrarรก uma abordagem de programaรงรฃo dinรขmica, a versรฃo recursiva deste mรฉtodo branch-and-bound, para resolver o problema do caixeiro viajante.
Programaรงao dinamica รฉ um mรฉtodo para buscar soluรงรตes รณtimas analisando todas as rotas possรญveis. ร um dos mรฉtodos de soluรงรฃo exatos que resolvem os problemas do caixeiro viajante atravรฉs de um custo relativamente mais elevado do que o mรฉtodos gananciosos que fornecem uma soluรงรฃo quase ideal.
A complexidade computacional desta abordagem รฉ SOBRE(N^2 * 2^N) que รฉ discutido posteriormente neste artigo.
O mรฉtodo do vizinho mais prรณximo รฉ uma abordagem gananciosa baseada em heurรญstica onde escolhemos o nรณ vizinho mais prรณximo. Esta abordagem รฉ computacionalmente mais barata que a abordagem dinรขmica. Mas nรฃo oferece a garantia de uma soluรงรฃo รณtima. Este mรฉtodo รฉ usado para soluรงรตes quase รณtimas.
Algoritmo para o problema do caixeiro viajante
Usaremos a abordagem de programaรงรฃo dinรขmica para resolver o Problema do Caixeiro Viajante (TSP).
Antes de iniciar o algoritmo, vamos conhecer algumas terminologias:
- Um grรกfico G=(V, E), que รฉ um conjunto de vรฉrtices e arestas.
- V รฉ o conjunto de vรฉrtices.
- E รฉ o conjunto de arestas.
- Os vรฉrtices sรฃo conectados por meio de arestas.
- Dist(i,j) denota a distรขncia nรฃo negativa entre dois vรฉrtices, i e j.
Vamos supor que S seja o subconjunto de cidades e pertenรงa a {1, 2, 3,โฆ, n} onde 1, 2, 3โฆn sรฃo as cidades e i, j sรฃo duas cidades nesse subconjunto. Agora cost(i, S, j) รฉ definido de tal forma como o comprimento do caminho mais curto que visita o nรณ em S, que tem exatamente uma vez o ponto inicial e final como i e j, respectivamente.
Por exemplo, custo (1, {2, 3, 4}, 1) denota o comprimento do caminho mais curto onde:
- A cidade inicial รฉ 1
- As cidades 2, 3 e 4 sรฃo visitadas apenas uma vez
- O ponto final รฉ 1
O algoritmo de programaรงรฃo dinรขmica seria:
- Defina cost(i, , i) = 0, o que significa que comeรงamos e terminamos em i, e o custo รฉ 0.
- Quando |S| > 1, definimos cost(i, S, 1) = โ onde i !=1 . Porque inicialmente nรฃo sabemos o custo exato para chegar da cidade i ร cidade 1 atravรฉs de outras cidades.
- Agora, precisamos comeรงar em 1 e completar o tour. Precisamos selecionar a prรณxima cidade de tal maneira-
custo (i, S, j) = custo mรญnimo (i, Sโ{i}, j)+dist(i,j) onde iโS e iโ j
Para a figura dada, a matriz de adjacรชncia seria a seguinte:
| dist(eu,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 |
Vamos ver como nosso algoritmo funciona:
Passo 1) Estamos considerando nossa jornada comeรงando na cidade 1, visitar outras cidades uma vez e retornar ร cidade 1.
Passo 2) S รฉ o subconjunto de cidades. De acordo com nosso algoritmo, para todo |S| > 1, definiremos o custo da distรขncia (i, S, 1) = โ. Aqui cost(i, S, j) significa que estamos comeรงando na cidade i, visitando as cidades de S uma vez, e agora estamos na cidade j. Definimos esse custo de caminho como infinito porque ainda nรฃo sabemos a distรขncia. Entรฃo os valores serรฃo os seguintes:
Custo (2, {3, 4}, 1) = โ ; a notaรงรฃo indica que estamos comeรงando na cidade 2, passando pelas cidades 3, 4 e chegando a 1. E o custo do caminho รฉ infinito. De forma similar-
custo(3, {2, 4}, 1) = โ
custo(4, {2, 3}, 1) = โ
Passo 3) Agora, para todos os subconjuntos de S, precisamos encontrar o seguinte:
custo (i, S, j) = custo mรญnimo (i, Sโ{i}, j)+dist(i,j), onde jโS e iโ j
Isso significa o caminho de custo mรญnimo para comeรงar em i, passar uma vez pelo subconjunto de cidades e retornar ร cidade j. Considerando que a viagem comeรงa na cidade 1, o custo รณtimo do caminho seria = custo(1, {outras cidades}, 1).
Vamos descobrir como poderรญamos conseguir isso:
Agora S = {1, 2, 3, 4}. Existem quatro elementos. Portanto, o nรบmero de subconjuntos serรก 2 ^ 4 ou 16. Esses subconjuntos sรฃo-
1) |S| = Nulo:
{ฮฆ}
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}}
Como estamos comeรงando em 1, poderรญamos descartar os subconjuntos que contรชm a cidade 1.
O cรกlculo do algoritmo:
1) |S| =ฮฆ:
custo (2, ฮฆ, 1) = dist(2, 1) = 10
custo (3, ฮฆ, 1) = dist(3, 1) = 15
custo (4, ฮฆ, 1) = dist(4, 1) = 20
2) |S| = 1:
custo (2, {3}, 1) = dist(2, 3) + custo (3, ฮฆ, 1) = 35+15 = 50
custo (2, {4}, 1) = dist(2, 4) + custo (4, ฮฆ, 1) = 25+20 = 45
custo (3, {2}, 1) = dist(3, 2) + custo (2, ฮฆ, 1) = 35+10 = 45
custo (3, {4}, 1) = dist(3, 4) + custo (4, ฮฆ, 1) = 30+20 = 50
custo (4, {2}, 1) = dist(4, 2) + custo (2, ฮฆ, 1) = 25+10 = 35
custo (4, {3}, 1) = dist(4, 3) + custo (3, ฮฆ, 1) = 30+15 = 45
3) |S| = 2:
custo (2, {3, 4}, 1) = min [ dist[2,3]+Custo(3,{4},1) = 35+50 = 85,
dist[2,4]+Custo(4,{3},1) = 25+45 = 70 ] = 70
custo (3, {2, 4}, 1) = min [ dist[3,2]+Custo(2,{4},1) = 35+45 = 80,
dist[3,4]+Custo(4,{2},1) = 30+35 = 65 ] = 65
custo (4, {2, 3}, 1) = min [ dist[4,2]+Custo(2,{3},1) = 25+50 = 75
dist[4,3]+Custo(3,{2},1) = 30+45 = 75 ] = 75
4) |S| = 3:
custo (1, {2, 3, 4}, 1) = min [ dist[1,2]+Custo(2,{3,4},1) = 10+70 = 80
dist[1,3]+Custo(3,{2,4},1) = 15+65 = 80
dist[1,4]+Custo(4,{2,3},1) = 20+75 = 95 ] = 80
Entรฃo a soluรงรฃo รณtima seria 1-2-4-3-1
Pseudo-cรณ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 ะ S and i โ 1
Cost (i, S, j) = min {Cost (i, S โ {i}, j) + dist(i, j) for j ะ S and i โ j}
Return min(i) Cost (i, {1, 2, 3, โฆ, n}, j) + d(j, i)
Implementaรงรฃo em C/C++
Aqui estรก a implementaรงรฃo em C++:
#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;
while(next_permutation(vertex.begin(), vertex.end()))
{
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);
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;
}
Saรญda:
80
Implementaรงรฃo em Python
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
next_permutation=permutations(vertex)
for i in next_permutation:
current_cost = 0
k = s
for j in i:
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))
Saรญda:
80
Soluรงรตes Acadรชmicas para TSP
Os cientistas da computaรงรฃo passaram anos procurando um algoritmo de tempo polinomial aprimorado para o Problema do Caixeiro Viajante. Atรฉ agora, o problema ainda รฉ NP-difรญcil.
Embora algumas das seguintes soluรงรตes tenham sido publicadas nos รบltimos anos, reduziram a complexidade atรฉ certo ponto:
- O TSP simรฉtrico clรกssico รฉ resolvido pelo Mรฉtodo do Sufixo Zero.
- O Algoritmo de Otimizaรงรฃo Baseado em Biogeografia baseia-se na estratรฉgia de migraรงรฃo para resolver os problemas de otimizaรงรฃo que podem ser planejados como TSP.
- O algoritmo evolutivo multiobjetivo foi projetado para resolver vรกrios TSP baseados em NSGA-II.
- O Sistema Multiagente resolve o TSP de N cidades com recursos fixos.
Aplicaรงรฃo do problema do caixeiro viajante
O Problema do Caixeiro Viajante (TSP) รฉ aplicado no mundo real tanto em sua forma mais pura quanto modificada. Alguns deles sรฃo:
- Planejamento, logรญstica e fabricaรงรฃo de microchips: Problemas de inserรงรฃo de chips surgem naturalmente na indรบstria de microchips. Esses problemas podem ser planejados como problemas do caixeiro viajante.
- A sequenciaรงรฃo de ADN: Uma ligeira modificaรงรฃo do problema do caixeiro viajante pode ser usada no sequenciamento de DNA. Aqui, as cidades representam os fragmentos de DNA e a distรขncia representa a medida de similaridade entre dois fragmentos de DNA.
- Astronomia: O Problema do Caixeiro Viajante รฉ aplicado por astrรดnomos para minimizar o tempo gasto na observaรงรฃo de vรกrias fontes.
- Problema de controle ideal: A formulaรงรฃo do problema do caixeiro viajante pode ser aplicada em problemas de controle รณtimo. Pode haver vรกrias outras restriรงรตes adicionadas.
Anรกlise de Complexidade do TSP
- Complexidade de tempo: Hรก um total de 2N subproblemas para cada nรณ. Portanto, o nรบmero total de subproblemas seria N*2N. Cada um dos subproblemas requer tempo linear para ser resolvido. Se o nรณ de origem nรฃo for especificado, teremos que executar loops para N nรณs.
Portanto, a complexidade de tempo total para uma soluรงรฃo ideal seria o Nรบmero de nรณs * Nรบmero de subproblemas * tempo para resolver cada subproblema. A complexidade do tempo pode ser definida como O(N2 * 2^N).
- Complexidade do espaรงo: A abordagem de programaรงรฃo dinรขmica usa memรณria para armazenar C(S, i), onde S รฉ um subconjunto do conjunto de vรฉrtices. Hรก um total de 2N subconjuntos para cada nรณ. Portanto, a complexidade do espaรงo รฉ O(2^N).
A seguir, vocรช aprenderรก sobre Algoritmo da Peneira de Eratรณstenes




