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.

Exemplo de TSP

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

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:

Algoritmo para o problema do caixeiro viajante

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

Algoritmo para o problema do caixeiro viajante

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

Resuma esta postagem com: