パート 1: はじめに
巡回セールスマン問題 (TSP) は、組み合わせ最適化における古典的な問題の 1 つで、セールスマンが各都市を一度訪問して元の場所に戻ることができる最短の移動ルートを見つけることが目的です。TSP は NP 困難な問題であるため、最適な解決策を見つけることは非常に困難です。ただし、シミュレーテッド アニーリングや 2-opt などのヒューリスティックを使用すると、かなり適切な近似解を見つけることができます。
この記事では、C++ を使用して両方のメソッドを実装する方法と、TSP ソリューションを視覚的に表現する視覚化ツールを提供する方法について説明します。また、シミュレーテッド アニーリングと 2-opt の原理と、それらを TSP にどのように適用できるかについても説明します。
巡回セールスマン問題の簡単な説明
TSP は数学的に次のように定義できます。都市のリストと各都市間の距離が与えられ、各都市を 1 回だけ訪問し、最後に開始都市に戻るような合計距離が最短の移動ルートを見つけます。
シミュレーテッドアニーリングアルゴリズムの概要
シミュレーテッド アニーリング アルゴリズムは、固体の冷却プロセスからインスピレーションを得ています。物理学では、物質の冷却プロセスはランダムですが、時間の経過とともにこのランダム性は減少し、システムはより秩序だった状態に向かう傾向があります。シミュレーテッド アニーリング アルゴリズムは、このプロセスをシミュレートし、探索空間内のランダム性を継続的に低減することによって問題に対する近似最適解を見つけます。
アルゴリズムの基本的な手順は次のとおりです。
- 溶液と高温を初期化します。
- 現在の温度で、次の手順を N 回繰り返します。
- 隣接するソリューションをランダムに選択します。
- 新しい溶液と現在の溶液のエネルギー差を計算します。
- 新しい解のエネルギーが低いか、特定の確率条件を満たしている場合、新しい解は受け入れられます。
- 温度を下げ、停止条件が満たされるまでステップ 2 を繰り返します。
2-opt アルゴリズムの概要
2-opt は、TSP のシンプルだが効果的なローカル検索戦略です。基本的な考え方は、現在の移動ルートで 2 つのエッジを見つけ、それらの端点を交換して新しい移動ルートを取得することです。
2-opt の基本的な手順は次のとおりです。
- 初期解はランダムに選択されます。
- 現在のソリューションで 2 つのエッジを見つけ、それらのエンドポイントを交換して、より短いルートを取得します。
- 改善できるエッジが見つからなくなるまで、ステップ 2 を繰り返します。
C++の実装
まず、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;
ここで、特定の移動ルートの合計距離を計算する関数が必要です。
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;
}
この時点で、シミュレーテッドアニーリングと 2-opt を実装するための基礎が確立されました。特定のアルゴリズムの実装と視覚化プロセスについては、完全なプロジェクトをダウンロードして表示してください。
パート II: シミュレーテッド アニーリングの実装
シミュレーテッドアニーリングアルゴリズムの実装を始めましょう。アルゴリズムの基本構造は次のとおりです。
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 オプションの実装
次に、2-opt アルゴリズムを実装します。
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;
}
2つを組み合わせる
より良い結果を得るには、まずシミュレーテッド アニーリング アルゴリズムを使用して適切な初期解を取得し、その後 2-opt を適用してさらに最適化します。
std::vector<int> combinedTSPSolution(const std::vector<City>& cities) {
auto initialSolution = simulatedAnnealing(cities, 10000, 0.995, 100000);
return twoOpt(cities, initialSolution);
}
この組み合わせたアプローチにより、どちらかのアルゴリズムを単独で使用するよりも優れたソリューションが得られることがよくあります。
次のセクションでは、上記のアルゴリズムに視覚化機能を追加する方法を検討します。これにより、ソリューションの進行状況をリアルタイムで確認し、より良い結果を得るためにアルゴリズム パラメーターをさらに最適化および調整する方法を分析できるようになります。さらに、プロジェクト全体のダウンロード リンクも提供します。
パート 3: 視覚化とさらなる最適化
視覚化
TSP ソリューションを効果的に視覚化するには、SFML や SDL などの C++ グラフィック ライブラリを使用できます。ここでは、SFML ライブラリを使用してソリューションの進捗状況を視覚化する方法を簡単に紹介します。
まず、SFML ライブラリをインストールする必要があります。次に、都市と道を描画するための単純なウィンドウを作成します。
#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();
}
}
}
この可視化機能は、都市と現在の最適なルートを表示します。シミュレーテッド アニーリングまたは 2-opt を繰り返すたびに、この関数を呼び出して、表示されるパスを更新できます。
高度な最適化
-
パラメータ調整: 模擬焼鈍の効果は、初期温度、冷却速度、反復回数などのパラメータに大きく依存します。問題の規模と複雑さに応じて調整が必要になる場合があります。
-
その他のヒューリスティック手法: シミュレーテッド アニーリングや 2-opt に加えて、TSP を解決するために使用できる遺伝的アルゴリズム、アリのコロニー最適化など、他の多くのヒューリスティック手法があります。
-
並列コンピューティング: シミュレーテッド アニーリングと 2-opt はどちらも反復ベースの手法であるため、簡単に並列化して速度を向上させることができます。アルゴリズムのパフォーマンスをさらに向上させるために、マルチスレッドまたは GPU アクセラレーションの使用を検討することもできます。
結論
TSP は、最も広く研究されている組み合わせ最適化問題の 1 つであり、物流、製造、航空など、多くの実際的な応用例があります。TSP は NP 困難問題ですが、シミュレーテッド アニーリング、2-opt、およびその他のヒューリスティックを使用することで、リアルタイムで非常に優れた近似解を得ることができます。