Optimizing the Traveling Salesman Problem: Visual Solution of Simulated Annealing and 2-opt Algorithm implemented in C++

Part One: Introduction

The Traveling Salesman Problem (TSP) is one of the classic problems in combinatorial optimization. Its goal is to find the shortest travel route so that a salesman can visit each city once and return to the origin. Since TSP is an NP-hard problem, it is very difficult to find its optimal solution. However, by using heuristics like simulated annealing and 2-opt, we can find reasonably good approximate solutions.

This article will guide you on how to implement both methods using C++ and provide a visualization tool to visually represent the TSP solution. We will also explore the principles of simulated annealing and 2-opt, and how they can be applied to TSP.

Brief description of traveling salesman problem

TSP can be mathematically defined as follows: given a list of cities and the distance between each pair of cities, find a travel route with the shortest total distance such that each city is visited only once and returns to the starting city at the end.

Introduction to simulated annealing algorithm

The simulated annealing algorithm is inspired by the cooling process of solids. In physics, the cooling process of matter is random, but over time, this randomness decreases and the system tends to a more orderly state. The simulated annealing algorithm simulates this process and finds the approximate optimal solution to the problem by continuously reducing the randomness in the search space.

The basic steps of the algorithm are as follows:

  1. Initialize a solution and a high temperature.
  2. At the current temperature, repeat the following steps N times:
    • Randomly select a neighbor solution.
    • Calculate the energy difference between the new solution and the current solution.
    • If the new solution has lower energy or meets certain probability conditions, the new solution is accepted.
  3. Lower the temperature and repeat step 2 until the stopping condition is met.

Introduction to 2-opt algorithm

2-opt is a simple but effective local search strategy for TSP. The basic idea is to find two edges in the current travel route and exchange their endpoints to get a new travel route.

The basic steps of 2-opt are as follows:

  1. The initial solution is chosen randomly.
  2. Find two edges in the current solution and swap their endpoints to get a shorter route.
  3. Repeat step 2 until no edges can be found that can be improved.

C++ implementation

First, we define the data structure of TSP:

struct City {
    
    
    int id;
    double x, y;
};

double distance(const City& c1, const City& c2) {
    
    
    return sqrt((c1.x - c2.x) * (c1.x - c2.x) + (c1.y - c2.y) * (c1.y - c2.y));
}

std::vector<City> cities;

Now, we need a function to calculate the total distance of a given travel route:

double totalDistance(const std::vector<int>& tour) {
    
    
    double dist = 0.0;
    for (size_t i = 0; i < tour.size() - 1; ++i) {
    
    
        dist += distance(cities[tour[i]], cities[tour[i+1]]);
    }
    dist += distance(cities[tour.back()], cities[tour[0]]);
    return dist;
}

At this point, we have established the basis for implementing simulated annealing and 2-opt. For specific algorithm implementation and visualization process, please download the complete project to view.

Part 2: Simulated annealing implementation

Let's start implementing the simulated annealing algorithm. The following is the basic structure of the algorithm:

std::vector<int> simulatedAnnealing(const std::vector<City>& cities, double initialTemperature, double coolingRate, int iterations) {
    
    
    int n = cities.size();
    std::vector<int> currentTour(n);
    std::iota(currentTour.begin(), currentTour.end(), 0);  // 初始化为0, 1, ..., n-1

    std::vector<int> bestTour = currentTour;
    double bestDist = totalDistance(bestTour);

    std::default_random_engine generator;
    std::uniform_real_distribution<double> distribution(0.0, 1.0);

    double temperature = initialTemperature;

    for (int i = 0; i < iterations; ++i) {
    
    
        std::vector<int> newTour = currentTour;
        
        // 随机选择两个城市并交换它们
        int a = rand() % n;
        int b = rand() % n;
        std::swap(newTour[a], newTour[b]);

        double currentDist = totalDistance(currentTour);
        double newDist = totalDistance(newTour);
        double deltaE = newDist - currentDist;

        // 如果新旅程更短或在某个概率下接受更长的旅程
        if (deltaE < 0 || distribution(generator) < exp(-deltaE / temperature)) {
    
    
            currentTour = newTour;
            currentDist = newDist;
        }

        if (currentDist < bestDist) {
    
    
            bestTour = currentTour;
            bestDist = currentDist;
        }

        temperature *= coolingRate;
    }

    return bestTour;
}

2-opt implementation

Next, we will implement the 2-opt algorithm:

std::vector<int> twoOpt(const std::vector<City>& cities, const std::vector<int>& initialTour) {
    
    
    int n = cities.size();
    std::vector<int> tour = initialTour;

    bool improved = true;
    while (improved) {
    
    
        improved = false;
        double bestDist = totalDistance(tour);

        for (int i = 0; i < n - 1; ++i) {
    
    
            for (int j = i + 1; j < n; ++j) {
    
    
                std::vector<int> newTour = tour;
                std::reverse(newTour.begin() + i, newTour.begin() + j);

                double newDist = totalDistance(newTour);
                if (newDist < bestDist) {
    
    
                    tour = newTour;
                    bestDist = newDist;
                    improved = true;
                }
            }
        }
    }

    return tour;
}

combine the two

In order to obtain better results, we can first use the simulated annealing algorithm to obtain a good initial solution, and then apply 2-opt for further optimization.

std::vector<int> combinedTSPSolution(const std::vector<City>& cities) {
    
    
    auto initialSolution = simulatedAnnealing(cities, 10000, 0.995, 100000);
    return twoOpt(cities, initialSolution);
}

This combined approach often yields better solutions than either algorithm alone.


In the next section, we will explore how to add visualization capabilities to the above algorithm, allowing us to see the progress of the solution in real time and analyze how to further optimize and adjust the algorithm parameters to obtain better results. Additionally, we will provide you with a download link for the complete project.

Part 3: Visualization and further optimization

Visualization

To effectively visualize TSP solutions, we can use C++ graphics libraries such as SFML or SDL. Here we will briefly introduce how to use the SFML library to visualize the progress of a solution.

First, you need to install the SFML library. We can then create a simple window to draw cities and paths.

#include <SFML/Graphics.hpp>

void visualizeTSPSolution(const std::vector<City>& cities, const std::vector<int>& tour) {
    
    
    sf::RenderWindow window(sf::VideoMode(800, 600), "TSP Visualization");
    window.clear(sf::Color::White);

    sf::CircleShape circle(5);
    circle.setFillColor(sf::Color::Blue);

    // 绘制城市
    for (const auto& city : cities) {
    
    
        circle.setPosition(city.x, city.y);
        window.draw(circle);
    }

    // 绘制路径
    sf::VertexArray lines(sf::LinesStrip, cities.size() + 1);
    for (size_t i = 0; i < tour.size(); ++i) {
    
    
        lines[i].position = sf::Vector2f(cities[tour[i]].x, cities[tour[i]].y);
        lines[i].color = sf::Color::Red;
    }
    lines[tour.size()].position = sf::Vector2f(cities[tour[0]].x, cities[tour[0]].y);  // Closing the loop
    lines[tour.size()].color = sf::Color::Red;

    window.draw(lines);
    window.display();

    while (window.isOpen()) {
    
    
        sf::Event event;
        while (window.pollEvent(event)) {
    
    
            if (event.type == sf::Event::Closed)
                window.close();
        }
    }
}

This visualization function will display the city and the current best route. On each iteration of simulated annealing or 2-opt, you can call this function to update the displayed path.

advanced optimization

  1. Parameter adjustment : The effect of simulated annealing depends largely on its parameters, such as initial temperature, cooling rate and number of iterations. You may need to adjust based on the size and complexity of the problem.

  2. Other heuristic methods : In addition to simulated annealing and 2-opt, there are many other heuristic methods, such as genetic algorithms, ant colony optimization, etc., that can be used to solve TSP.

  3. Parallel Computing : Since simulated annealing and 2-opt are both iteration-based methods, they can be easily parallelized to increase speed. You may consider using multi-threading or GPU acceleration to further improve the performance of your algorithm.

Conclusion

TSP is one of the most widely studied combinatorial optimization problems, and it has many practical applications such as logistics, manufacturing and aviation. Although TSP is an NP-hard problem, by using simulated annealing, 2-opt, and other heuristics, we can obtain very good approximate solutions in real time.

Guess you like

Origin blog.csdn.net/qq_38334677/article/details/132606939