Um caixeiro precisa visitar n cidades exatamente uma vez cada e voltar para a cidade de origem, percorrendo a menor distância (ou custo) total possível.
É representado como um grafo completo ponderado (todas as cidades têm ligação direta com todas as outras, com um custo/distância).
O objetivo é encontrar o ciclo hamiltoniano de menor custo.
Algoritmo
Algoritmo: Caixeiro Viajante
Entrada: Matriz de distâncias entre n elementos.
Inicializar:
Conjunto de pontos = {1,2,...,n}
MelhorCusto=∞
MelhorRota=[ ]
Para cada permutação P de pontos:
a. Calcular Custo(P)
b. Se Custo(P)<MelhorCusto:
MelhorCusto=Custo(P)
Implementação da Instância do Problema
O trabalho propõe a implementação e análise computacional de uma instância do Problema do Caixeiro Viajante aplicada ao contexto de deslocamento entre capitais brasileiras.
Estrutura do Grafo e Coordenadas
O problema foi modelado como um grafo completo \(G=(V,E)\), onde:
\(V\) representa o conjunto de vértices (capitais)
\(E\) representa o conjunto de arestas (ligações entre capitais)
Cada aresta possui um peso associado correspondente à distância entre duas capitais
As coordenadas geográficas de cada capital foram utilizadas para calcular as distâncias entre os vértices por meio da fórmula de Haversine, que permite estimar a distância entre dois pontos na superfície terrestre.
Estrutura do Grafo e Coordenadas
Dessa forma, o grafo gerado é:
Completo (todas as capitais estão conectadas entre si)
Ponderado (cada conexão possui um custo)
Não direcionado (distâncias simétricas)
Implementação do Algoritmo de Referência (BASELINE)
A abordagem segue os seguintes passos:
Fixação da cidade inicial (Brasília);
Geração de todas as permutações possíveis das demais capitais;
Construção das rotas completas (ida e volta);
Cálculo da distância total de cada rota;
Seleção da rota com menor custo
Implementação do Algoritmo de Referência (BASELINE)
Análise de Complexidade
O algoritmo de força bruta possui complexidade temporal:
\[
O(n!)
\]
É necessário calcular a soma das distâncias entre as cidades, o que possui custo:
\[
O(n)
\]
Assim, a complexidade total pode ser representada como:
\[
O(n . n!)
\]
Implementação do Algoritmo de Referência (BASELINE)
Resultados
Análise de complexidade e tempo de execução das rotas.
Teste
Nº de Capitais
Nº de Possibilidades
Rotas Testadas
% do Espaço
Tempo (s)
1
5
120
120
100%
0,01
2
8
40.320
40.320
100%
2,09
3
9
362.880
362.880
100%
21,27
4
10
3.628.800
3.628.000
100%
232,53
5
13
6.227.020.800
20.892.479
0,3355%
1.800
6
27
1,09 × 10²⁸
67.081.582
~0,0000%
10.800
Implementação do Algoritmo de Referência (BASELINE)
Resultados
Implementação do Algoritmo de Referência (BASELINE)
Taxa de Processamento
Podemos estimar a taxa média de processamento: Exemplo (teste 13 capitais):
Mesmo com essa taxa seriam necessários bilhões de anos para explorar todo o espaço das 27 capitais.
Implementação e Ambiente de Execução
A implementação foi realizada em linguagem Python 3.x.
import pandas as pdimport itertoolsimport timeimport math# ============================================================# CAIXEIRO VIAJANTE - FORÇA BRUTA COM CONDIÇÃO DE PARADA# Ponto inicial: Brasília# Para quando atingir um tempo máximo em minutos# ============================================================ARQUIVO_CSV ="matriz_distancias_capitais_brasil.csv"CAPITAL_INICIAL ="Brasília"# Quantidade de capitais mais próximas de Brasília que serão testadasQUANTIDADE_CAPITAIS =5# Tempo máximo de execução em minutosTEMPO_LIMITE_MINUTOS =180print("="*70)print("CAIXEIRO VIAJANTE - FORÇA BRUTA COM TEMPO LIMITE")print("="*70)print("\n[1] Carregando matriz de distâncias...")matriz = pd.read_csv(ARQUIVO_CSV, index_col=0)print("Arquivo carregado com sucesso!")print(f"Total de capitais disponíveis: {len(matriz)}")print("\n[2] Selecionando capitais mais próximas de Brasília...")distancias_brasilia = matriz.loc[CAPITAL_INICIAL]distancias_brasilia = distancias_brasilia.drop(CAPITAL_INICIAL)capitais_proximas = distancias_brasilia.sort_values().head(QUANTIDADE_CAPITAIS)capitais_para_visitar =list(capitais_proximas.index)print(f"\nCapital inicial: {CAPITAL_INICIAL}")print(f"Quantidade de capitais escolhidas: {QUANTIDADE_CAPITAIS}")print("\nCapitais selecionadas:")for i, capital inenumerate(capitais_para_visitar, start=1):print(f"{i}. {capital} - {capitais_proximas[capital]} km de Brasília")print("\n[3] Calculando quantidade total de possibilidades...")total_possibilidades = math.factorial(QUANTIDADE_CAPITAIS)print(f"Total de rotas possíveis: {total_possibilidades:,}")print(f"Tempo limite: {TEMPO_LIMITE_MINUTOS} minuto(s)")def calcular_distancia_rota(rota, matriz_distancias):""" Calcula a distância total de uma rota. A rota começa em Brasília, passa pelas capitais e retorna para Brasília. """ distancia_total =0for i inrange(len(rota) -1): origem = rota[i] destino = rota[i +1] distancia_total += matriz_distancias.loc[origem, destino]return distancia_totalprint("\n[4] Iniciando busca por força bruta...")tempo_limite_segundos = TEMPO_LIMITE_MINUTOS *60inicio_tempo = time.time()menor_distancia =float("inf")melhor_rota =Nonerotas_testadas =0# Não usamos list(), porque para 27 capitais isso ocuparia muita memória.# O itertools.permutations gera uma rota por vez.for rota_permutada in itertools.permutations(capitais_para_visitar): tempo_atual = time.time() tempo_decorrido = tempo_atual - inicio_tempo# Condição de parada por tempoif tempo_decorrido >= tempo_limite_segundos:print("\nTempo limite atingido.")break rota_completa = [CAPITAL_INICIAL] +list(rota_permutada) + [CAPITAL_INICIAL] distancia_total = calcular_distancia_rota(rota_completa, matriz) rotas_testadas +=1if distancia_total < menor_distancia: menor_distancia = distancia_total melhor_rota = rota_completaprint("\nNova melhor rota encontrada!")print(f"Rota número: {rotas_testadas}")print(f"Distância: {menor_distancia:.2f} km")print(" -> ".join(melhor_rota))# Mostra progresso a cada 10.000 rotas testadasif rotas_testadas %10000==0: percentual = (rotas_testadas / total_possibilidades) *100print("\nProgresso:")print(f"Rotas testadas: {rotas_testadas:,}")print(f"Percentual aproximado: {percentual:.8f}%")print(f"Tempo decorrido: {tempo_decorrido:.2f} segundos")print(f"Melhor distância até agora: {menor_distancia:.2f} km")fim_tempo = time.time()tempo_total = fim_tempo - inicio_tempoprint("\n"+"="*70)print("RESULTADO FINAL")print("="*70)print(f"\nCapital inicial: {CAPITAL_INICIAL}")print(f"Capitais visitadas: {QUANTIDADE_CAPITAIS}")print(f"\nTotal de rotas possíveis: {total_possibilidades:,}")print(f"Rotas testadas: {rotas_testadas:,}")percentual_final = (rotas_testadas / total_possibilidades) *100print(f"Percentual testado: {percentual_final:.8f}%")print(f"\nTempo limite configurado: {TEMPO_LIMITE_MINUTOS} minuto(s)")print(f"Tempo real de execução: {tempo_total:.2f} segundos")print("\nMelhor rota encontrada dentro do tempo:")print(" -> ".join(melhor_rota))print(f"\nMenor distância encontrada: {menor_distancia:.2f} km")print("\n"+"="*70)print("FIM DO PROCESSAMENTO")print("="*70)