整数二分算法和浮点数二分算法

embedded/2024/9/23 1:17:07/

整数二分算法和浮点数二分算法


二分

现实中运用到二分的就是猜数字的游戏 假如有A同学说B同学所说数的大小,B同学要在1~100中间猜中数字65,当B同学每次说的数都是范围的一半时这就算是一个二分查找的过程
在这里插入图片描述

二分查找的前提是这个数字序列要有单调性

基本步骤

初始化:

设定两个指针,left 和 right,分别指向数组的起始和末尾。

计算中间位置:

使用公式 mid = left + (right - left) / 2 或者left+right>>1计算中间位置。

比较:

如果中间位置的元素等于目标值,返回中间位置。
如果目标值小于中间位置的元素,则将 right 更新为 mid - 1,继续在左半部分查找。
如果目标值大于中间位置的元素,则将 left 更新为 mid + 1,继续在右半部分查找。

重复:

重复步骤 2 和 3,直到 left 超过 right。

结束:

如果在查找过程中未找到目标值,返回一个表示未找到的结果(如 -1 或 None)。

二分查找算法的时间复杂度是 O(log n),非常高效。

整数二分

二分的本质并不是一定要单调,而是对一个区间可以化分成两个部分,一部分一定满足条件,另一部分一定不满足,对于满足这种条件的我们可以找出两个边界点,这样的话二分算法可以寻找这个性质的边界(红色和黑色的边界都行,因为是整数二分所以两边界不重合)。
在这里插入图片描述

第一种情况(二分左半部分)

假如说有一串数字1,2,3,3,4,4,5,6,8,8我们需要找到满足小于等于3的最大情况的子序列,也就是我们需要找到最后一次出现的3,我们可以如何做呢?
我们让一个mid=(l+r+1)/2 假如说a[mid]<=3,此时if(check(mid))==true说明此时mid指向的值可能是答案,但是我们无法保证其后面还有没有答案是<=3的,所以此时应该是l=mid,假如说此时a[mid]>=3,if(check(mid))==false说明此时mid指向的是一定不满足条件的的那么此时应该是r=mid-1
在这里插入图片描述
我们继续不段重复以上操作直到l>=r时退出循环。

第二种情况(二分右半部分)

还是这个数字序列这次我们要找1,2,3,3,4,4,5,6,8,8中第一次出现3的位置,我们应该怎么做呢?
我们还是让一个mid=(l+r)/2 假如说a[mid]>=3,此时if(check(mid))==true说明此时mid指向的值可能是答案,但是我们无法保证其前面还有没有答案是>=3的,所以此时应该是r=mid,假如说此时a[mid]<=3,if(check(mid))==false说明此时mid指向的是一定不满足条件的的那么此时应该是l=mid+1

注意:第一种情况是(l+r+1)而不是(l+r)为为什么呢?

因为计算机的除法都是向下取整的所以就会出现问题,假如说此时l=r-1那么mid=(l+r)/2=(l+l+1)/2=l然后我们假如发现l还是满足条件的,那么此时就会陷入l=mid,mid=l的死循环

我们来写一道题

洛谷P2249
下面的代码是既有第一次出现,也有最后一次出现的,两种情况都写了。
代码如下:

#include <iostream>
using namespace std;const int N = 1e5 + 10; // 定义数组的最大容量,数组a最多可以存放1e5个元素
int a[N], n, m; // 定义全局变量数组a,n为数组长度,m为查询次数// check1函数用于检查a[mid]是否大于等于目标值x
bool check1(int mid, int x) {if (a[mid] >= x) {return true; // 如果a[mid]大于等于x,返回true,表示满足条件} else {return false; // 否则返回false,表示不满足条件}
}// check2函数用于检查a[mid]是否小于等于目标值x
bool check2(int mid, int x) {if (a[mid] <= x) {return true; // 如果a[mid]小于等于x,返回true,表示满足条件} else {return false; // 否则返回false,表示不满足条件}
}int main() {cin >> n >> m; // 输入数组的长度n和查询的次数mfor (int i = 1; i <= n; i++) {cin >> a[i]; // 依次输入数组a的元素}while (m--) { // 对每一次查询进行处理,m次查询int x; // 定义要查询的目标值xcin >> x; // 输入目标值x// 首先进行二分查找,寻找第一个大于等于x的位置int l = 1, r = n; // 初始化左右边界,l是左边界,r是右边界while (l < r) { // 当左边界小于右边界时,继续二分查找int mid = (l + r) >> 1; // 计算中间位置midif (check1(mid, x)) { // 如果a[mid]大于等于xr = mid; // 缩小右边界至mid} else {l = mid + 1; // 否则缩小左边界至mid+1}}// 查找完成后,检查a[l]是否等于目标值xif (a[l] == x) {cout << l << " "; // 如果a[l]等于x,输出位置l} else {cout << -1 << " "; // 如果a[l]不等于x,输出-1表示未找到}// 再进行一次二分查找,寻找最后一个小于等于x的位置l = 1, r = n; // 重新初始化左右边界while (l < r) {int mid = (l + r + 1) >> 1; // 计算中间位置mid,向上取整if (check2(mid, x)) { // 如果a[mid]小于等于xl = mid; // 缩小左边界至mid} else {r = mid - 1; // 否则缩小右边界至mid-1}}// 查找完成后,再次检查a[l]是否等于目标值xif (a[l] == x) {cout << l << " "; // 如果a[l]等于x,输出找到的最后位置l} else {cout << -1 << " "; // 如果没找到,输出-1}cout<<endl;}
}

整数二分模板

bool check(int x) {// 这里是判断x是否满足某种性质的函数,具体实现取决于实际问题// 可以根据x的值来返回true或false,用于二分查找中的判断/* ... */
}// 区间[l, r]被划分为[l, mid]和[mid + 1, r]时使用的二分查找
int bsearch_1(int l, int r) {// 二分查找的目的是在区间[l, r]中寻找满足某种性质的最小位置// l 是左边界,r 是右边界,最终返回满足性质的最小下标while (l < r) { // 当左边界小于右边界时,继续进行二分查找int mid = (l + r) >> 1; // 计算中间位置mid,使用右移操作进行快速计算,相当于 (l + r) / 2if (check(mid)) { // 如果check(mid)为true,表示mid满足性质r = mid; // 将右边界缩小到mid,因为我们要找满足性质的最小位置} else { // 否则,mid不满足性质l = mid + 1; // 将左边界缩小到mid + 1,因为mid以及它左边的值都不满足条件}}return l; // 返回最终的左边界,此时l == r,且为满足性质的最小位置
}// 区间[l, r]被划分为[l, mid - 1]和[mid, r]时使用的二分查找
int bsearch_2(int l, int r) {// 二分查找的目的是在区间[l, r]中寻找满足某种性质的最大位置// l 是左边界,r 是右边界,最终返回满足性质的最大下标while (l < r) { // 当左边界小于右边界时,继续进行二分查找int mid = (l + r + 1) >> 1; // 计算中间位置mid,并向上取整,确保mid偏向右侧if (check(mid)) { // 如果check(mid)为true,表示mid满足性质l = mid; // 将左边界缩小到mid,因为我们要找满足性质的最大位置} else { // 否则,mid不满足性质r = mid - 1; // 将右边界缩小到mid - 1,因为mid以及它右边的值都不满足条件}}return l; // 返回最终的左边界,此时l == r,且为满足性质的最大位置
}

浮点数二分算法

浮点数二分相较于整数二分更加简单因为只有一个模板,并且没有边界问题,浮点数的二分查找可以用于求解需要精确值的问题,例如求方程的解或几何问题中涉及浮点精度的求解。与整数二分查找不同,浮点数二分查找必须考虑精度问题,因为浮点数无法精确到某个具体值,所以我们需要一个精度(如 epsilon),用于判断二分查找的终止条件。

假如说我们需要找一个数x的平方等于目标值2

代码如下:

#include <iostream>
#include <cmath> // 包含abs函数,用于计算绝对值
using namespace std;// 定义一个需要使用二分法求解的函数,返回值为目标函数值
double f(double x) {// 举例:寻找函数 f(x) = x^2 - 2 的根return x * x - 2;
}int main() {double l = 0, r = 2; // 初始区间[l, r],假设根位于[0, 2]之间double eps = 1e-7;   // 定义精度eps,即当结果误差小于1e-7时停止迭代// 当区间宽度大于精度要求时,继续二分while (r - l > eps) {double mid = (l + r) / 2; // 计算中间值if (f(mid) > 0) { // 如果 f(mid) > 0,表示 mid 处的值在根的右侧r = mid; // 缩小右边界至mid} else { // 否则 f(mid) <= 0,表示 mid 处的值在根的左侧或是根l = mid; // 缩小左边界至mid}}// 输出结果,l 和 r 最终都会逼近根cout << "x ≈ " << l << endl;cout << "验证结果: f(x) = " << f(l) << endl; // 输出验证f(l)接近0
}

浮点数二分模板

// 函数原型:在浮点数区间 [l, r] 上使用二分查找,找到满足某种性质的浮点数
double bsearch_3(double l, double r)
{const double eps = 1e-6;   // eps 是精度控制的参数,当区间长度小于 eps 时停止二分查找// 1e-6 表示小数点后 6 位精度,可根据题目要求调整精度// 当区间长度大于 eps 时,继续进行二分查找while (r - l > eps){// 计算区间的中点 middouble mid = (l + r) / 2;// 调用 check 函数判断 mid 是否满足给定的性质// 假设 check(mid) 返回 true,则意味着 mid 及其右侧可能满足性质,// 因此将右区间收缩到 mid,继续在左侧区间 [l, mid] 上搜索if (check(mid)) r = mid;// 否则,mid 及其左侧不满足性质,因此我们将左区间收缩到 mid,继续在右侧区间 [mid, r] 上搜索else l = mid;}// 最后返回左边界 l(或右边界 r),此时区间已经很小,接近于精度要求的结果// 因为 l 和 r 的距离非常小,最终答案应为 l 或 r 的近似值return l;
}

整数二分算法和浮点数二分算法源代码


http://www.ppmy.cn/embedded/115334.html

相关文章

KDD 2024论文分享┆STAMP:一种基于时空图神经网络的微服务工作负载预测方法

论文分享简介 本推文详细介绍了一篇最新论文成果《Integrating System State into Spatio Temporal Graph Neural Network for Microservice Workload Prediction》&#xff0c;论文的作者包括&#xff1a;上海交通大学先进网络实验室: 罗旸、高墨涵、余哲梦&#xff0c;高晓沨…

Spring Boot-跨服务事务管理问题

Spring Boot 跨服务事务管理问题及其解决方案 1. 引言 在微服务架构中&#xff0c;应用被拆分成多个独立的服务&#xff0c;这些服务通常通过 HTTP、消息队列或 gRPC 等方式相互通信。在某些场景下&#xff0c;一个业务流程需要在多个服务之间进行操作&#xff0c;每个服务会…

Java迭代器Iterator和Iterable有什么区别?

在 Java 中&#xff0c;我们对 List 进行遍历的时候&#xff0c;主要有这么三种方式。 第一种&#xff1a;for 循环。 for (int i 0; i < list.size(); i) {System.out.print(list.get(i) "&#xff0c;"); } 第二种&#xff1a;迭代器。 Iterator it list.i…

Kali root密码忘记的解决方法

Kali root密码忘记的解决方法 uname -a: Linux xkm 5.10.0-21-amd64 #1 SMP Debian 5.10.162-1 (2023-01-21) x86_64 GNU/Linux背景 许久未用的虚拟机&#xff0c;密码忘了&#xff0c;按照有的搜索结果操作竟然不行&#xff08;那篇是乱写的&#xff09;。 按照这篇博客&a…

19 基于51单片机的倒计时音乐播放系统设计

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 五个按键&#xff0c;分别为启动按键&#xff0c;则LCD1602显示倒计时&#xff0c;音乐播放 设置按键&#xff0c;可以设置倒计时的分秒&#xff0c;然后加减按键&#xff0c;还有最后一个暂停音乐…

Mysql学习

目录 1、常用命令&#xff1a; 2、SQL语言分类 2.1 DML语言 2.2 DDL语言 2.3 DCL语言 2.4 SQL语言注意事项 3、数据处理之查询 3.1 基本的 SELECT 语句 3.2 过滤数据 3.2.1 比较运算符 a、将表中第lien2 列中不等于90的列过滤掉 b、选择表中salary 列中小于3000的…

Give azure openai an encyclopedia of information

题意&#xff1a;给 Azure OpenAI 提供一部百科全书式的信息 问题背景&#xff1a; I am currently dabbling in the Azure OpenAI service. I want to take the default model and knowledge base and now add on to it my own unique information. So, for example, for mak…

GPU使用

0. 写这篇文章的背景 最近还是在使用GPU、连接远程服务器上出现了一点问题,发现在这方面的知识还是学得很模糊。(最让人感到困惑的是之前GPU的使用都没有问题) 总结一下最近的问题: 1.每一次连接远程服务器(选择的Ubuntu22.04),使用服务器的文件夹还好(关键是现在用…