爬山算法的詳細介紹

目錄

🍉概述

🍉 步驟

🍉 優缺點

🍈優點

🍈缺點

🍈應對策略

🍉示例

🍈旅行商問題

🍍步驟

🍍分解代碼

🍎包含頭文件

🍎定義函數(計算總距離)

🍎生成初始解

🍎生成鄰域解

🍎爬山算法

🍎主函數

🍍完整代碼

🍍代碼說明

🍍代碼分析

🍎時間復雜度分析

🍎空間復雜度分析

🍉結論


🍉概述

  • 爬山算法(Hill Climbing Algorithm)是一種啟發式搜索算法,用于尋找給定問題的局部最優解。它是一種貪婪算法,每一步都選擇當前狀態中最佳的鄰居狀態,直到不能再找到更好的鄰居為止。
  • 爬山算法通常用于優化問題中,如函數優化、路徑規劃等。盡管它可能會陷入局部最優解,但其簡單性和有效性使得它在許多實際應用中得到廣泛使用。

🍉 步驟

  1. 初始化:隨機選擇或者指定一個初始解作為起點。

  2. 評估:計算當前位置的解的價值或者成本,即目標函數的值。

  3. 鄰域搜索:尋找當前位置的鄰居解,即通過微小的變化產生相鄰的解。

  4. 移動:選擇最好的鄰居解作為下一步的位置。如果該鄰居解比當前解更優,則移動到該鄰居解;否則算法停止,當前解即為局部最優解。

  5. 終止條件:根據特定條件判斷是否達到停止搜索的條件,例如達到一定迭代次數、解的改進不顯著等。

🍉 優缺點

🍈優點

  • 簡單易懂:爬山算法的概念和實現都非常簡單,易于理解和編碼。這使得它成為許多初學者和快速原型開發的首選算法。

  • 計算效率高:由于每一步只需要比較有限的鄰域解,爬山算法的單步計算速度較快,在搜索空間較小或結構較簡單的問題上能快速收斂。

  • 局部優化:在許多實際問題中,找到一個局部最優解已經可以滿足需求。爬山算法擅長在局部區域內找到較好的解。

🍈缺點

  • 容易陷入局部最優解:爬山算法很容易陷入局部最優解,而無法找到全局最優解。尤其是在復雜或具有多個局部最優解的問題上,這一缺點尤為明顯。

  • 缺乏全局視野:爬山算法沒有跳出局部最優解的機制,因此在具有復雜地形(多峰)的搜索空間中,性能較差。

  • 對初始解敏感:算法的最終結果依賴于初始解的選擇,不同的初始解可能會導致不同的局部最優解。

  • 不適用于大規模問題:對于大規模問題(如具有大量變量或復雜約束的優化問題),爬山算法的效果不佳,因為它無法有效探索大規模搜索空間。

🍈應對策略

為克服爬山算法的這些缺點,通常可以結合其他優化策略,如

  • 模擬退火算法(Simulated Annealing):通過允許偶爾接受較差的解來跳出局部最優解。
  • 遺傳算法(Genetic Algorithm):通過模擬自然選擇和遺傳變異的過程來探索更廣闊的解空間。
  • 禁忌搜索(Tabu Search):通過記錄最近訪問過的解,避免重復搜索和陷入局部最優。
  • 多次運行:多次運行爬山算法,每次使用不同的隨機初始解,從而增加找到全局最優解的概率。

🍉示例

🍈旅行商問題

旅行商問題(TSP)是一個經典的組合優化問題,目標是在一組城市中找到最短的閉環路徑,使得每個城市僅訪問一次并最終回到起點城市。

🍍步驟

  1. 初始化:隨機生成一個可行解,即生成一個隨機的城市訪問順序。
  2. 評估:計算當前路徑的總距離。
  3. 鄰域搜索:生成當前路徑的鄰域解。鄰域可以通過交換路徑中的兩個城市順序、逆序某段路徑等方式生成。
  4. 移動:選擇鄰域中距離最短的路徑作為新的當前路徑,如果新路徑比當前路徑更優,則移動到新路徑;否則,算法停止。
  5. 終止條件:達到指定的迭代次數或沒有更優的鄰域解。

🍍分解代碼

🍎包含頭文件
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <climits>using namespace std;
🍎定義函數(計算總距離)
double calculateTotalDistance(const vector<int>& solution, const vector<vector<double>>& distanceMatrix) {double totalDistance = 0;for (size_t i = 0; i < solution.size(); ++i) {totalDistance += distanceMatrix[solution[i]][solution[(i + 1) % solution.size()]];}return totalDistance;
}
🍎生成初始解
vector<int> initialSolution(int numCities) {vector<int> solution(numCities);for (int i = 0; i < numCities; ++i) {solution[i] = i;}random_shuffle(solution.begin(), solution.end());return solution;
}
🍎生成鄰域解
vector<vector<int>> getNeighbors(const vector<int>& solution) {vector<vector<int>> neighbors;for (size_t i = 0; i < solution.size(); ++i) {for (size_t j = i + 1; j < solution.size(); ++j) {vector<int> neighbor = solution;swap(neighbor[i], neighbor[j]);neighbors.push_back(neighbor);}}return neighbors;
}
🍎爬山算法
pair<vector<int>, double> hillClimbing(const vector<vector<double>>& distanceMatrix, int maxIterations) {int numCities = distanceMatrix.size();vector<int> currentSolution = initialSolution(numCities);double currentDistance = calculateTotalDistance(currentSolution, distanceMatrix);for (int iteration = 0; iteration < maxIterations; ++iteration) {vector<vector<int>> neighbors = getNeighbors(currentSolution);vector<int> newSolution = currentSolution;double newDistance = currentDistance;for (const auto& neighbor : neighbors) {double neighborDistance = calculateTotalDistance(neighbor, distanceMatrix);if (neighborDistance < newDistance) {newSolution = neighbor;newDistance = neighborDistance;}}if (newDistance < currentDistance) {currentSolution = newSolution;currentDistance = newDistance;} else {break;}}return {currentSolution, currentDistance};
}
🍎主函數
int main() {srand(time(0));// 示例:城市間的距離矩陣vector<vector<double>> distanceMatrix = {{0, 29, 20, 21},{29, 0, 15, 17},{20, 15, 0, 28},{21, 17, 28, 0}};int maxIterations = 1000;auto result = hillClimbing(distanceMatrix, maxIterations);vector<int> optimalPath = result.first;double optimalDistance = result.second;cout << "Optimal path: ";for (int city : optimalPath) {cout << city << " ";}cout << endl;cout << "Total distance: " << optimalDistance << endl;return 0;
}

🍍完整代碼

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <climits>using namespace std;// 計算總距離函數
// 傳入參數:solution(當前城市訪問順序),distanceMatrix(城市間距離矩陣)
// 返回值:當前路徑的總距離
double calculateTotalDistance(const vector<int>& solution, const vector<vector<double>>& distanceMatrix) {double totalDistance = 0;for (size_t i = 0; i < solution.size(); ++i) {// 計算從當前城市到下一個城市的距離(環形路徑)totalDistance += distanceMatrix[solution[i]][solution[(i + 1) % solution.size()]];}return totalDistance;
}// 生成初始解函數
// 傳入參數:numCities(城市數量)
// 返回值:隨機生成的城市訪問順序
vector<int> initialSolution(int numCities) {vector<int> solution(numCities);for (int i = 0; i < numCities; ++i) {solution[i] = i;}// 隨機打亂城市訪問順序random_shuffle(solution.begin(), solution.end());return solution;
}// 生成鄰域解函數
// 傳入參數:solution(當前城市訪問順序)
// 返回值:鄰域解的集合(通過交換兩個城市順序生成)
vector<vector<int>> getNeighbors(const vector<int>& solution) {vector<vector<int>> neighbors;for (size_t i = 0; i < solution.size(); ++i) {for (size_t j = i + 1; j < solution.size(); ++j) {vector<int> neighbor = solution;// 交換兩個城市的順序swap(neighbor[i], neighbor[j]);neighbors.push_back(neighbor);}}return neighbors;
}// 爬山算法函數
// 傳入參數:distanceMatrix(城市間距離矩陣),maxIterations(最大迭代次數)
// 返回值:最優解及其對應的總距離
pair<vector<int>, double> hillClimbing(const vector<vector<double>>& distanceMatrix, int maxIterations) {int numCities = distanceMatrix.size();// 生成初始解vector<int> currentSolution = initialSolution(numCities);// 計算初始解的總距離double currentDistance = calculateTotalDistance(currentSolution, distanceMatrix);for (int iteration = 0; iteration < maxIterations; ++iteration) {// 生成當前解的鄰域解vector<vector<int>> neighbors = getNeighbors(currentSolution);vector<int> newSolution = currentSolution;double newDistance = currentDistance;// 尋找鄰域中最優的解for (const auto& neighbor : neighbors) {double neighborDistance = calculateTotalDistance(neighbor, distanceMatrix);if (neighborDistance < newDistance) {newSolution = neighbor;newDistance = neighborDistance;}}// 如果找到更優解,更新當前解if (newDistance < currentDistance) {currentSolution = newSolution;currentDistance = newDistance;} else {// 否則退出循環break;}}return {currentSolution, currentDistance};
}int main() {srand(time(0));  // 初始化隨機數種子// 示例:城市間的距離矩陣vector<vector<double>> distanceMatrix = {{0, 29, 20, 21},{29, 0, 15, 17},{20, 15, 0, 28},{21, 17, 28, 0}};int maxIterations = 1000;  // 最大迭代次數auto result = hillClimbing(distanceMatrix, maxIterations);  // 調用爬山算法vector<int> optimalPath = result.first;  // 最優路徑double optimalDistance = result.second;  // 最優路徑的總距離// 輸出最優路徑cout << "Optimal path: ";for (int city : optimalPath) {cout << city << " ";}cout << endl;// 輸出最優路徑的總距離cout << "Total distance: " << optimalDistance << endl;return 0;
}

🍍代碼說明

  1. 頭文件:包含必要的頭文件。
  2. 計算總距離:calculateTotalDistance 函數計算路徑的總距離。
  3. 生成初始解:initialSolution 函數生成一個隨機的城市訪問順序。
  4. 生成鄰域解:getNeighbors 函數生成當前解的鄰域解。
  5. 爬山算法:hillClimbing 函數實現爬山算法,尋找旅行商問題的最優解。
  6. 主函數:main 函數中定義了距離矩陣,并調用爬山算法找到最優路徑和距離。

🍍代碼分析

🍎時間復雜度分析

計算總距離函數 calculateTotalDistance

double calculateTotalDistance(const vector<int>& solution, const vector<vector<double>>& distanceMatrix)
  • 該函數需要遍歷路徑中的所有城市,因此其時間復雜度為 O(n),其中 n 是城市的數量。

生成初始解函數 initialSolution

vector<int> initialSolution(int numCities)
  • 該函數需要初始化一個大小為 n 的向量,并對其進行隨機打亂。初始化向量的時間復雜度為 O(n),隨機打亂的時間復雜度為 O(n),因此總的時間復雜度為O(n)。

生成鄰域解函數 getNeighbors

vector<vector<int>> getNeighbors(const vector<int>& solution)
  • 該函數生成所有可能的鄰域解,對于每對城市交換一次,生成一個新的鄰域解。因此,其時間復雜度為O(n^2)。

爬山算法函數 hillClimbing

pair<vector<int>, double> hillClimbing(const vector<vector<double>>& distanceMatrix, int maxIterations)

該函數的主要步驟包括:

  • 生成初始解,時間復雜度為 O(n)。
  • 在每次迭代中:
    • 生成鄰域解,時間復雜度為 O(n^2)。
    • 遍歷鄰域解并計算每個解的總距離,時間復雜度為 O(n^3)(每個鄰域解計算距離為 O(n),有 O(n^2) 個鄰域解)。
  • 迭代最多 maxIterations 次。

因此,爬山算法的總時間復雜度為 O(maxIterations×n^3)。

🍎空間復雜度分析

計算總距離函數 calculateTotalDistance

  • 該函數使用常量空間,空間復雜度為 O(1)。

生成初始解函數 initialSolution

  • 該函數返回一個大小為 n 的向量,因此空間復雜度為 O(n)。

生成鄰域解函數 getNeighbors

  • 該函數生成所有鄰域解,最多有 O(n^2) 個鄰域解,每個鄰域解的大小為 n。因此,空間復雜度為 O(n^3)。

爬山算法函數 hillClimbing

  • 該函數的主要空間開銷來自:
  • 因此,爬山算法的總空間復雜度為 O(n^3)。
  • 初始解和當前解的存儲,空間復雜度為 O(n)。
  • 鄰域解的存儲,空間復雜度為 O(n^3)。

這些復雜度分析表明,爬山算法在解決旅行商問題時,對于較大的城市數量 �n 以及較高的迭代次數 maxIterations,可能會遇到計算和存儲資源的限制。


🍉結論

  • 爬山算法作為一種簡單、快速的局部搜索優化算法,在解決一些中小規模問題時非常有效。然而,由于其容易陷入局部最優解且缺乏全局視野,在復雜或大規模優化問題上,需要結合其他優化策略以提升性能和解決問題的能力。


希望這些能對剛學習算法的同學們提供些幫助哦!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/15553.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/15553.shtml
英文地址,請注明出處:http://en.pswp.cn/web/15553.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Cortex-M3的SysTick 定時器

目錄 概述 1 SysTick 定時器 1.1 SysTick 定時器功能介紹 1.2 SysTick 定時器功能實現 1.3 SysTick在系統中的作用 2 SysTick應用的實例 2.1 建立異常服務例程 2.2 使能異常 2.3 鬧鐘功能 2.4 重定位向量表 2.5 消滅二次觸發 3 SysTick在FreeRTOS中的應用 3.1 STM…

【代碼】結構體

哈嘍大家好&#xff0c;我是學霸小羊&#xff0c;今天講講結構體。 先看例題&#xff1a; 例1.老師給了小楊一份同學們的考試成績&#xff0c;包括語數英三科&#xff0c;老師讓小明按照總分排序&#xff0c;請你幫幫他吧&#xff01; 輸入數據&#xff1a; 第1行 學生總人…

在docker中運行SLAM十四講程序

《十四講》的示例程序依賴比較多&#xff0c;而且系統有點舊。可以在容器中運行。 拉取鏡像 docker pull ddhogan/slambook:v0.1這個docker對應的github&#xff1a;HomeLH/slambook2-docker 拉下來之后&#xff0c;假如是Windows系統&#xff0c;需要使用XLaunch用于提供X11…

面試大雜燴之kafka

面試這個領域最近環境不行&#xff0c;所以卷起來流量挺大 關于K8s 其實看我之前的博客&#xff0c;k8s剛有點苗頭的時候我就研究過&#xff0c;然后工作的時候間接接觸 也自己玩過 但是用的不多就忘記了&#xff0c;正苦于不知道寫什么&#xff0c;水一篇 用來面試應該是夠了…

C++ | Leetcode C++題解之第111題二叉樹的最小深度

題目&#xff1a; 題解&#xff1a; class Solution { public:int minDepth(TreeNode *root) {if (root nullptr) {return 0;}queue<pair<TreeNode *, int> > que;que.emplace(root, 1);while (!que.empty()) {TreeNode *node que.front().first;int depth que…

VC編譯sample_onnx_mnist提示無法打開輸入文件cudnn.lib

出現錯誤 LNK1181 無法打開輸入文件“C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1\lib\x64\cudnn.lib” 解決辦法&#xff1a;下載cudnn&#xff0c;NVIDIA cuDNN | NVIDIA Developer 拷貝相應的文件到CUDA安裝的目錄下。 VC編譯libtorch提示無法打開輸入文件cu…

huggingface 筆記:PretrainModel

1 from_pretrained 從預訓練模型配置中實例化一個 PyTorch 預訓練模型默認情況下&#xff0c;模型使用 model.eval() 設置為評估模式&#xff08;Dropout 模塊被禁用&#xff09; 要訓練模型&#xff0c;應該首先使用 model.train() 將其設置回訓練模式 1.1 主要參數 pretra…

java 子類繼承父類

為什么需要繼承 我現在要有兩個類一個 一個是小學生&#xff0c;一個是大學生 代碼 小學生 package b; public class encapsulatio{public String name;public int age;public double score;public void setscore (double score) {this.scorescore;}public void testing() {S…

(三)MySQL 索引

歡迎訪問 什么是索引&#xff1f; 提高查詢效率的一種數據結構&#xff0c;索引是數據的目錄 索引的分類 按「數據結構」分類&#xff1a;Btree索引、Hash索引、Full-text索引。按「物理存儲」分類&#xff1a;聚簇索引、二級索引。按「字段特性」分類&#xff1a;主鍵索引…

Spring6 對 集成MyBatis 開發運用(附有詳細的操作步驟)

詳細實現操作步驟 具體實現內容&#xff1a;我們運用 Spring6 和 MyBatis 實現一個轉賬操作(該轉賬操作&#xff0c;進行一個事務上的控制&#xff0c;運用 MyBatis 執行 SQL 語句)。 第一步&#xff1a;準備數據庫表 使用t_act表&#xff08;賬戶表&#xff09; 連接數據庫的…

三個有意思的鏈表面試題的完成

上一篇博客我們已經完成了鏈表的所有內容&#xff0c;那么這一篇博客我們來看一下三個特別有意思的鏈表題目。 **第一個題目如下&#xff1a;**相信不少朋友看到這題目就已經暈了&#xff0c;那就簡單說明下這個題目&#xff0c;題目就是創建一個鏈表&#xff0c;其中每個節點…

深入探索Java中的流式編程:優雅地處理集合數據

Java流式編程&#xff08;Stream API&#xff09;是Java 8引入的一項重要特性&#xff0c;它為處理集合數據提供了一種更為優雅和函數式的方式。通過流式操作&#xff0c;開發者可以以更簡潔、更直觀的方式處理數據&#xff0c;從而提高代碼的可讀性和可維護性。本文將深入探討…

Android14 - 繪制系統 - 概覽

從Android 12開始&#xff0c;Android的繪制系統有結構性變化&#xff0c; 在繪制的生產消費者模式中&#xff0c;新增BLASTBufferQueue&#xff0c;客戶端進程自行進行queue的生產和消費&#xff0c;隨后通過Transation提交到SurfaceFlinger&#xff0c;如此可以使得各進程將緩…

【vue3+elementuiplus】el-select下拉框會自動觸發校驗規則

場景&#xff1a;編輯彈框省份字段下拉框必填&#xff0c;觸發方式change&#xff0c;有值第一次打開不會觸發校驗提示&#xff0c;關閉彈框再次打開觸發必填校驗提示&#xff0c;但是該字段有值 問題的原因是&#xff1a;在關閉彈層事件中&#xff0c;我做了resetfileds&…

SpringBoot + MybatisPlus

SpringBoot MybatisPlus 整合記錄 1. 硬件軟件基本信息2. 相關鏈接3. 通過idea快速生成一個Springboot項目4. 啟動報錯問題解決問題一&#xff1a;Springboot啟動的時候報錯提示 “沒有符合條件的Bean關于Mapper類型”問題二&#xff1a;啟動的時候提示需要一個Bean&#xff0…

電磁仿真--CST網格介紹

1. 簡介 網格會影響仿真的準確性和速度&#xff0c;花時間理解網格化過程是很重要的。 CST 中可用的數值方法包括FIT、TLM、FEM、MoM&#xff0c;使用不同類型的網格&#xff1a; FIT和TLM&#xff1a;六面體 FEM&#xff1a;四面體、平面 MoM&#xff1a;表面 CFD&#…

深入理解與防御跨站腳本攻擊(XSS):從搭建實驗環境到實戰演練的全面教程

跨站腳本攻擊&#xff08;XSS&#xff09;是一種常見的網絡攻擊手段&#xff0c;它允許攻擊者在受害者的瀏覽器中執行惡意腳本。以下是一個XSS攻擊的實操教程&#xff0c;包括搭建實驗環境、編寫測試程序代碼、挖掘和攻擊XSS漏洞的步驟。 搭建實驗環境 1. 安裝DVWA&#xff…

【408真題】2009-16

“接”是針對題目進行必要的分析&#xff0c;比較簡略&#xff1b; “化”是對題目中所涉及到的知識點進行詳細解釋&#xff1b; “發”是對此題型的解題套路總結&#xff0c;并結合歷年真題或者典型例題進行運用。 涉及到的知識全部來源于王道各科教材&#xff08;2025版&…

推薦一個快速開發接私活神器

文章目錄 前言一、項目介紹二、項目地址三、功能介紹四、頁面顯示登錄頁面菜單管理圖表展示定時任務管理用戶管理代碼生成 五、視頻講解總結 前言 大家好&#xff01;我是智航云科技&#xff0c;今天為大家分享一個快速開發接私活神器。 一、項目介紹 人人開源是一個提供多種…

SCSS配置教程

SCSS&#xff08;Sassy CSS&#xff09;是 Sass&#xff08;Syntactically Awesome Stylesheets&#xff09;的一種語法&#xff0c;它是一種 CSS 預處理器&#xff0c;允許你使用變量、嵌套規則、混合&#xff08;mixin&#xff09;、函數等高級功能來編寫 CSS&#xff0c;從而…