旅行商問題(TSP)概述

tegou發表於2024-10-12

旅行商問題(TSP)概述

1. TSP問題的複雜性

定義:旅行商問題(Traveling Salesman Problem, TSP)是給定一系列城市及其之間的距離,要求找到一條最短路徑,使得旅行商從某個城市出發,經過每個城市恰好一次並返回到起點城市。

複雜性分析:

  • TSP是一個NP-hard問題,這意味著目前沒有已知的多項式時間演算法可以解決所有例項。
  • 隨著城市數量的增加,可能的路徑組合呈指數增長。例如,對於n個城市,可能的路徑數為(n-1)!/2。
  • 因此,對於較小的城市數(通常n ≤ 20),可以使用暴力搜尋或動態規劃方法;對於較大的城市數,常用啟發式或近似演算法。

2. 貪心法的主要思路和求解步驟

貪心法簡介:

貪心演算法透過每一步選擇中都採取當前狀態下最好或最優的選擇,從而希望得到全域性最好或最優解。

求解步驟:

  • 初始化:選擇一個起始城市作為當前城市,並將其標記為已訪問。
  • 選擇下一個城市:在未訪問城市中選擇與當前城市距離最短的城市,並移動到該城市。
  • 重複:繼續選擇下一個城市,直到所有城市都被訪問過。
  • 返回起點:最後從最後一個城市返回起始城市,完成環路。

3. 兩種貪心法求解TSP的近似演算法

演算法1:最近鄰點演算法

步驟:

  • 從起始城市出發,標記為已訪問。
  • 在未訪問的城市中選擇距離當前城市最近的城市,並移動到該城市。
  • 重複步驟2,直到所有城市均已訪問。
  • 返回起始城市。

上程式碼

#include<bits/stdc++.h> 
using namespace std;

const int MAX = 10; // 最大城市數量
double dist[MAX][MAX]; // 城市距離矩陣
bool visited[MAX]; // 標記是否訪問過

// 找到最近的未訪問城市
int findNearestCity(int current, int n) {
    double minDist = numeric_limits<double>::max();
    int nearestCity = -1;

    for (int i = 1; i <= n; ++i) {
        if (!visited[i] && dist[current][i] < minDist) {
            minDist = dist[current][i];
            nearestCity = i;
        }
    }

    return nearestCity;
}

// 實現最近鄰演算法
vector<int> nearestNeighbor(int start, int n) {
    vector<int> path;
    fill(visited, visited + n, false);
    int current = start;
    visited[current] = true;
    path.push_back(current);

    for (int i = 1; i < n; ++i) {
        current = findNearestCity(current, n);
        visited[current] = true;
        path.push_back(current);
    }
    
    path.push_back(start); // 返回起點
    return path;
}

int main() {
    int n;
    cin >> n;

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cin >> dist[i][j];
        }
    }

    vector<int> path = nearestNeighbor(1, n);

    cout << "最近鄰演算法路徑: ";
    for (int city : path) {
        cout << city << " ";
    }
    cout << endl;

    return 0;
}

演算法2:貪婪插入演算法

步驟:

  • 初始化一個路徑,包括任意兩個城市。
  • 每次從未訪問城市中選擇一個城市,插入到當前路徑中的最佳位置,以使得路徑長度最小。
  • 重複步驟2,直到所有城市均已訪問。
  • 返回起始城市。

上程式碼

#include<bits/stdc++.h> 
using namespace std;

vector<int> greedyInsertTSP(const vector<vector<int>>& distanceMatrix) {
    int n = distanceMatrix.size();
    vector<bool> visited(n, false);
    vector<int> path;

    int currentCity = 0;
    visited[currentCity] = true;
    path.push_back(currentCity);

    for (int i = 1; i < n; ++i) {
        // 找到最近的未訪問城市
        int nearestCity = -1;
        int minDistance = numeric_limits<int>::max();

        for (int j = 0; j < n; ++j) {
            if (!visited[j] && distanceMatrix[currentCity][j] < minDistance) {
                minDistance = distanceMatrix[currentCity][j];
                nearestCity = j;
            }
        }

        // 插入最近的城市到路徑中
        path.push_back(nearestCity);
        visited[nearestCity] = true;
        currentCity = nearestCity;
    }
    // 完成迴圈,返回起始城市
    path.push_back(path[0]); // 返回到起始城市

    return path;
}

int main() {
    int n;
    cin >> n;
    vector<vector<int>> distanceMatrix(n, vector<int>(n));
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> distanceMatrix[i][j];
        }
    }
    vector<int> result = greedyInsertTSP(distanceMatrix);

    for (int city : result) {
        cout << city << " ";
    }
    cout << endl;

    return 0;
}

4. 解的對比分析

  • 效率:
    • 最近鄰演算法簡單易實現,但可能會陷入區域性最優。
    • 貪婪插入演算法相對複雜一些,通常能提供更好的近似解,因為它考慮了整體路徑的影響。
  • 解的質量:
    • 最近鄰演算法的路徑通常較短,但不一定是最優解。
    • 貪婪插入演算法透過插入策略,可以獲得更接近實際最優解的路徑。
  • 時間複雜度:
    • 最近鄰演算法的時間複雜度為O(n^2)。
    • 貪婪插入演算法的時間複雜度為O(n^2),但由於需要查詢最佳插入位置,實際時間可能略高於最近鄰演算法。

相關文章