Problema del commesso viaggiatore: Python, C++ Algoritmo

Cosโ€™รจ il problema del commesso viaggiatore (TSP)?

Il problema del commesso viaggiatore (TSP) รจ un classico problema combinatorio dell'informatica teorica. Il problema chiede di trovare il percorso piรน breve in un grafo con la condizione di visitare tutti i nodi una sola volta e ritornare alla cittร  di origine.

La dichiarazione del problema fornisce un elenco di cittร  insieme alle distanze tra ciascuna cittร .

Obiettivo: Per iniziare dalla cittร  di origine, visita le altre cittร  una sola volta e torna nuovamente alla cittร  di origine. Il nostro obiettivo รจ trovare il percorso piรน breve possibile per completare il percorso di andata e ritorno.

Esempio di TSP

Qui viene fornito un grafico in cui 1, 2, 3 e 4 rappresentano le cittร  e il peso associato a ciascun bordo rappresenta la distanza tra tali cittร .

Esempio di TSP

L'obiettivo รจ trovare il percorso piรน breve possibile per il tour che parte dalla cittร  di origine, attraversa il grafico visitando le altre cittร  o nodi una sola volta e ritorna alla cittร  di origine.

Per il grafico sopra, il percorso ottimale รจ seguire il percorso del costo minimo: 1-2-4-3-1. E questo percorso piรน breve costerebbe 10+25+30+15 =80

Soluzioni diverse al problema del commesso viaggiatore

Soluzioni diverse al problema del commesso viaggiatore

Il problema del commesso viaggiatore (TSP) รจ classificato come un problema NP-difficile perchรฉ non ha un algoritmo in tempo polinomiale. La complessitร  aumenta esponenzialmente aumentando il numero di cittร .

Esistono diversi modi per risolvere il problema del commesso viaggiatore (cucchiaino). Alcune soluzioni popolari sono:

Lโ€™approccio della forza bruta รจ il metodo ingenuo per risolvere i problemi dei venditori ambulanti. In questo approccio, prima calcoliamo tutti i percorsi possibili e poi li confrontiamo. Il numero di percorsi in un grafico composto da n cittร  รจ n! Risolvere il problema del commesso viaggiatore con questo approccio basato sulla forza bruta รจ molto costoso dal punto di vista computazionale.

Il metodo branch-and-bound: In questo approccio il problema viene suddiviso in sottoproblemi. La soluzione di questi singoli sottoproblemi fornirebbe una soluzione ottimale.

Questo tutorial dimostrerร  un approccio di programmazione dinamica, la versione ricorsiva di questo metodo branch-and-bound, per risolvere il problema del venditore ambulante.

Programmazione dinamica รจ un metodo per cercare soluzioni ottimali analizzando tutti i percorsi possibili. รˆ uno dei metodi di soluzione esatta che risolve i problemi dei venditori ambulanti a costi relativamente piรน elevati rispetto a quelli del venditore ambulante metodi avidi che forniscono una soluzione quasi ottimale.

La complessitร  computazionale di questo approccio รจ O(N^2 * 2^N) di cui si parlerร  piรน avanti in questo articolo.

Il metodo del vicino piรน prossimo รจ un approccio greedy basato su euristiche in cui scegliamo il nodo vicino piรน vicino. Questo approccio รจ computazionalmente meno costoso dell'approccio dinamico. Ma non fornisce la garanzia di una soluzione ottimale. Questo metodo รจ utilizzato per soluzioni quasi ottimali.

Algoritmo per il problema del commesso viaggiatore

Utilizzeremo l'approccio di programmazione dinamica per risolvere il problema del commesso viaggiatore (TSP).

Prima di iniziare l'algoritmo, familiarizziamo con alcune terminologie:

  • Un grafo G=(V, E), che รจ un insieme di vertici e archi.
  • V รจ l'insieme dei vertici.
  • E รจ l'insieme degli archi.
  • I vertici sono collegati tramite bordi.
  • Dist(i,j) denota la distanza non negativa tra due vertici, i e j.

Supponiamo che S sia il sottoinsieme delle cittร  e appartenga a {1, 2, 3, โ€ฆ, n} dove 1, 2, 3โ€ฆn sono le cittร  e i, j sono due cittร  in quel sottoinsieme. Ora cost(i, S, j) รจ definito in modo tale come la lunghezza del percorso piรน breve che visita il nodo in S, che esattamente una volta ha il punto iniziale e finale rispettivamente come i e j.

Ad esempio, il costo (1, {2, 3, 4}, 1) denota la lunghezza del percorso piรน breve dove:

  • La cittร  di partenza รจ 1
  • Le cittร  2, 3 e 4 vengono visitate una sola volta
  • Il punto finale รจ 1

L'algoritmo di programmazione dinamica sarebbe:

  • Imposta costo(i, , i) = 0, il che significa che iniziamo e finiamo da i e il costo รจ 0.
  • Quando |S| > 1, definiamo costo(i, S, 1) = โˆ dove i !=1 . Perchรฉ inizialmente non conosciamo il costo esatto per raggiungere la cittร  i fino alla cittร  1 attraverso altre cittร .
  • Ora dobbiamo iniziare da 1 e completare il tour. Dobbiamo selezionare la cittร  successiva in questo modo-

costo(i, S, j)=costo minimo (i, Sโˆ’{i}, j)+dist(i,j) dove iโˆˆS e iโ‰ j

Per la figura data, la matrice di adiacenza sarebbe la seguente:

Algoritmo per il problema del commesso viaggiatore

dist(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

Vediamo come funziona il nostro algoritmo:

Passo 1) Stiamo considerando il nostro viaggio partendo dalla cittร  1, visitando altre cittร  una volta e tornando alla cittร  1.

Passo 2) S รจ il sottoinsieme delle cittร . Secondo il nostro algoritmo, per tutti i |S| > 1, imposteremo la distanza cost(i, S, 1) = โˆ. Qui cost(i, S, j) significa che stiamo iniziando dalla cittร  i, visitando le cittร  di S una volta, e ora siamo nella cittร  j. Impostiamo questo costo del percorso come infinito perchรฉ non conosciamo ancora la distanza. Quindi i valori saranno i seguenti:

Costo (2, {3, 4}, 1) = โˆ ; la notazione indica che stiamo iniziando dalla cittร  2, attraversando le cittร  3, 4 e raggiungendo 1. E il costo del percorso รจ infinito. Allo stesso modo-

costo(3, {2, 4}, 1) = โˆ

costo(4, {2, 3}, 1) = โˆ

Passo 3) Ora, per tutti i sottoinsiemi di S, dobbiamo trovare quanto segue:

costo(i, S, j)=costo minimo (i, Sโˆ’{i}, j)+dist(i,j), dove jโˆˆS e iโ‰ j

Ciรฒ significa il percorso di costo minimo per iniziare da i, attraversare una volta il sottoinsieme di cittร  e tornare alla cittร  j. Considerando che il viaggio inizia nella cittร  1, il costo del percorso ottimale sarebbe= costo(1, {altre cittร }, 1).

Scopriamo come potremmo raggiungere questo obiettivo:

Ora S = {1, 2, 3, 4}. Ci sono quattro elementi. Quindi il numero di sottoinsiemi sarร  2 ^ 4 o 16. Questi sottoinsiemi sono-

1) |S| = Nullo:

{ฮฆ}

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}}

Dato che iniziamo da 1, potremmo scartare i sottoinsiemi contenenti la cittร  1.

Il calcolo dell'algoritmo:

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

Quindi la soluzione ottimale sarebbe 1-2-4-3-1

Algoritmo per il problema del commesso viaggiatore

Pseudo-codice

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) 

Implementazione in C/C++

Ecco l'implementazione in 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;
}

Produzione:

80

implementazione in 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))

Produzione:

80

Soluzioni accademiche al TSP

Gli informatici hanno trascorso anni alla ricerca di un algoritmo polinomiale migliorato per il problema del commesso viaggiatore. Finora, il problema รจ ancora NP-difficile.

Sebbene negli ultimi anni siano state pubblicate alcune delle seguenti soluzioni che hanno ridotto la complessitร  in una certa misura:

  • Il classico TSP simmetrico viene risolto con il metodo del suffisso zero.
  • Lโ€™algoritmo di ottimizzazione basato sulla biogeografia si basa sulla strategia di migrazione per risolvere i problemi di ottimizzazione che possono essere pianificati come TSP.
  • L'algoritmo evolutivo multi-obiettivo รจ progettato per risolvere piรน TSP basati su NSGA-II.
  • Il Sistema Multi-Agente risolve il TSP di N cittร  con risorse fisse.

Applicazione del problema del commesso viaggiatore

Il problema del commesso viaggiatore (TSP) viene applicato nel mondo reale sia nella sua forma piรน pura che modificata. Alcuni di questi sono:

  • Pianificazione, logistica e produzione di microchip: I problemi di inserimento dei chip sorgono naturalmente nell'industria dei microchip. Questi problemi possono essere pianificati come problemi del commesso viaggiatore.
  • Sequenziamento del DNA: Una leggera modifica del problema del commesso viaggiatore puรฒ essere utilizzata nel sequenziamento del DNA. Qui le cittร  rappresentano i frammenti di DNA e la distanza rappresenta la misura di somiglianza tra due frammenti di DNA.
  • Astronomia: Il problema del commesso viaggiatore viene applicato dagli astronomi per ridurre al minimo il tempo trascorso a osservare varie sorgenti.
  • Problema di controllo ottimo: Venditore ambulante La formulazione del problema puรฒ essere applicata a problemi di controllo ottimo. Potrebbero essere aggiunti molti altri vincoli.

Analisi della complessitร  del TSP

  • Complessitร  temporale: Ce ne sono un totale di 2N sottoproblemi per ciascun nodo. Quindi il numero totale di sottoproblemi sarebbe N*2N. Ciascuno dei sottoproblemi richiede tempo lineare per essere risolto. Se il nodo di origine non รจ specificato, dobbiamo eseguire cicli per N nodi.

    Quindi la complessitร  temporale totale per una soluzione ottimale sarebbe il numero di nodi * numero di sottoproblemi * tempo per risolvere ogni sottoproblema. La complessitร  temporale puรฒ essere definita come O(N2 *2^N).

  • Complessitร  spaziale: L'approccio di programmazione dinamica utilizza la memoria per memorizzare C(S, i), dove S รจ un sottoinsieme dell'insieme dei vertici. Ce ne sono in totale 2N sottoinsiemi per ogni nodo. Quindi, la complessitร  dello spazio รจ O(2^N).

Successivamente imparerai a conoscere Algoritmo del crivello di Eratostene

Riassumi questo post con: