爬山算法的详细介绍

目录

🍉概述

🍉 步骤

🍉 优缺点

🍈优点

🍈缺点

🍈应对策略

🍉示例

🍈旅行商问题

🍍步骤

🍍分解代码

🍎包含头文件

🍎定义函数(计算总距离)

🍎生成初始解

🍎生成邻域解

🍎爬山算法

🍎主函数

🍍完整代码

🍍代码说明

🍍代码分析

🍎时间复杂度分析

🍎空间复杂度分析

🍉结论


🍉概述

  • 爬山算法(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.mzph.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…

在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…

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;其中每个节点…

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;今天为大家分享一个快速开发接私活神器。 一、项目介绍 人人开源是一个提供多种…

Golang | Leetcode Golang题解之第112题路径总和

题目&#xff1a; 题解&#xff1a; func hasPathSum(root *TreeNode, sum int) bool {if root nil {return false}if root.Left nil && root.Right nil {return sum root.Val}return hasPathSum(root.Left, sum - root.Val) || hasPathSum(root.Right, sum - roo…

C++常见知识点总结

常见字符 * 注释&#xff1a;/* 这是一个注释*/乘法&#xff1a;a * b取值运算符&#xff1a;*指针变量&#xff0c;int a 4&#xff0c;*a &#xff1f;&#xff1f;&#xff1f;&#xff1f;指针变量&#xff1a;数据类型 *变量名&#xff0c; int *no &bh&#xff0…

SAP揭秘者-怎么执行生产订单ATP检查及其注意点

文章摘要&#xff1a; 上篇文章给大家介绍生产订单ATP检查的相关后台配置&#xff0c;大家可以按照配置步骤去进行配置&#xff0c;配置完之后&#xff0c;我们接下来就是要执行ATP检查。本篇文章具体给大家介绍怎么来执行生产 订单ATP检查及其注意点。 执行生产订单ATP检查的…

Qt for android 获取USB设备列表(二)JNI方式 获取

简介 基于上篇 [Qt for android 获取USB设备列表&#xff08;一&#xff09;Java方式 获取]&#xff0c; 这篇就纯粹多了&#xff0c; 直接将上篇代码转换成JNI方式即可。即所有的设备连接与上篇一致。 (https://listentome.blog.csdn.net/article/details/139205850) 关键代码…