使用遺傳演算法解決旅行商問題


旅行商問題 (TSP) 旨在找到連線一系列城市並返回起點的最短路徑。由於其組合性質以及隨著城市數量增加而呈指數級增長的路徑數量,這是一個難題。遺傳演算法 (GA) 是一種受遺傳學啟發的啟發式演算法。它模擬自然選擇來有效地解決 TSP 問題。GA 使用路徑來表示城市巡遊的候選方案。選擇、交叉和變異在 GA 中進化種群。選擇偏向適應性更高的路徑,這表示其質量或接近理想解的程度。變異引入隨機修改以探索新的解空間,而交叉則混合來自父路徑的遺傳資訊以產生子代。

使用的方法

  • 遺傳演算法

遺傳演算法

強大的啟發式遺傳演算法 (GA) 受到自然選擇和遺傳學的啟發。它模仿進化來有效地解決 TSP 問題。GA 中的每條路徑都是一個可能的解決方案。適應度決定了路徑的質量和最優性。GA 透過選擇、交叉和變異適應度值較高的路徑來發展新的種群。

演算法

  • 定義城市、最大世代數、種群大小和變異率。

  • 定義一個城市結構,包含 x 和 y 座標。

  • 定義一個路徑結構,包含城市索引向量(路徑)和適應度值。

  • 建立一個使用座標確定城市距離的方法。

  • 建立一個透過交換城市索引來建立隨機路徑的函式。

  • 建立一個對城市距離求和以計算路徑適應度的函式。

  • 建立一個將兩條父路徑交叉以建立子路徑的函式。

  • 透過基於變異率的函式交換城市來變異路徑。

  • 實現一個基於適應度的路徑查詢工具。

示例

#include <iostream>
#include <vector>
#include <algorithm>
#include <random>

using namespace std;

// Define the number of cities
const int NUM_CITIES = 5;

// Define the maximum generations for the GA
const int MAX_GENERATIONS = 100;

// Define the population size for the GA
const int POPULATION_SIZE = 10;

// Define the mutation rate for the GA
const double MUTATION_RATE = 0.1;

// Define a structure to represent a city
struct City {
    int x;
    int y;
};

// Define a structure to represent a route
struct Route {
    vector<int> path;
    double fitness;
};

// Calculate the distance between two cities
double calculateDistance(const City& city1, const City& city2) {
    int dx = city1.x - city2.x;
    int dy = city1.y - city2.y;
    return sqrt(dx*dx + dy*dy);
}

// Generate a random route
Route generateRandomRoute() {
    Route route;
    for (int i = 0; i < NUM_CITIES; ++i) {
        route.path.push_back(i);
    }
    random_shuffle(route.path.begin(), route.path.end());
    return route;
}

// Calculate the fitness of a route (smaller distance is better)
void calculateFitness(Route& route, const vector<City>& cities) {
    double totalDistance = 0.0;
    for (int i = 0; i < NUM_CITIES - 1; ++i) {
        int cityIndex1 = route.path[i];
        int cityIndex2 = route.path[i+1];
        totalDistance += calculateDistance(cities[cityIndex1], cities[cityIndex2]);
    }
    // Add distance from last city back to the starting city
    int lastCityIndex = route.path[NUM_CITIES - 1];
    totalDistance += calculateDistance(cities[lastCityIndex], cities[route.path[0]]);
    route.fitness = totalDistance;
}

// Perform crossover between two parent routes to produce a child route
Route crossover(const Route& parent1, const Route& parent2) {
    Route child;
    int startPos = rand() % NUM_CITIES;
    int endPos = rand() % NUM_CITIES;

    for (int i = 0; i < NUM_CITIES; ++i) {
        if (startPos < endPos && i > startPos && i < endPos) {
            child.path.push_back(parent1.path[i]);
        }
        else if (startPos > endPos && !(i < startPos && i > endPos)) {
            child.path.push_back(parent1.path[i]);
        }
        else {
            child.path.push_back(-1);
        }
    }

    for (int i = 0; i < NUM_CITIES; ++i) {
        if (find(child.path.begin(), child.path.end(), parent2.path[i]) == child.path.end()) {
            for (int j = 0; j < NUM_CITIES; ++j) {
                if (child.path[j] == -1) {
                    child.path[j] = parent2.path[i];
                    break;
                }
            }
        }
    }

    return child;
}

// Mutate a route by swapping two cities
void mutate(Route& route) {
    for (int i = 0; i < NUM_CITIES; ++i) {
        if ((double)rand() / RAND_MAX < MUTATION_RATE) {
            int swapIndex = rand() % NUM_CITIES;
            swap(route.path[i], route.path[swapIndex]);
        }
    }
}

// Find the best route in a population
Route findBestRoute(const vector<Route>& population) {
    double bestFitness = numeric_limits<double>::max();
    int bestIndex = -1;
    for (int i = 0; i < POPULATION_SIZE; ++i) {
        if (population[i].fitness < bestFitness) {
            bestFitness = population[i].fitness;
            bestIndex = i;
        }
    }
    return population[bestIndex];
}

int main() {
    // Define the cities
    vector<City> cities = {
        {0, 0},
        {1, 2},
        {3, 1},
        {4, 3},
        {2, 4}
    };

    // Initialize the population
    vector<Route> population;
    for (int i = 0; i < POPULATION_SIZE; ++i) {
        population.push_back(generateRandomRoute());
        calculateFitness(population[i], cities);
    }

    // Perform the GA iterations
    for (int generation = 0; generation < MAX_GENERATIONS; ++generation) {
        vector<Route> newPopulation;

        // Generate offspring through selection, crossover, and mutation
        for (int i = 0; i < POPULATION_SIZE; ++i) {
            Route parent1 = findBestRoute(population);
            Route parent2 = findBestRoute(population);
            Route child = crossover(parent1, parent2);
            mutate(child);
            calculateFitness(child, cities);
            newPopulation.push_back(child);
        }

        // Replace the old population with the new population
        population = newPopulation;
    }

    // Find the best route
    Route bestRoute = findBestRoute(population);

    // Print the best route
    cout << "Best Route: ";
    for (int i = 0; i < NUM_CITIES; ++i) {
        cout << bestRoute.path[i] << " ";
    }
    cout << endl;

    // Print the total distance of the best route
    cout << "Total Distance: " << bestRoute.fitness << endl;

    return 0;
}

輸出

Best Route: 2 3 4 1 0 
Total Distance: 12.1065

結論

最後,遺傳演算法 (GA) 可以解決旅行商問題 (TSP) 和其他組合最佳化問題。GA 迭代地搜尋可行解的廣闊搜尋空間,利用遺傳學和進化概念改進路徑適應度並找到一個良好的解。GA 對 TSP 的處理平衡了探索和利用。透過選擇、交叉和變異,GA 促進了更好路徑的發展並保持種群多樣性。GA 可以有效地搜尋解空間並避免區域性最優解。

更新於:2023年7月14日

4K+ 瀏覽量

開啟您的職業生涯

完成課程獲得認證

開始學習
廣告