搜索算法:Fibonacci查找
### 什么是Fibonacci查找
Fibonacci查找是一种搜索算法,它结合了Fibonacci数列和二分查找的思想,用于在有序数组中查找目标值。它的主要优点是在某些情况下可以比普通二分查找更高效。
### Fibonacci数列
Fibonacci数列是一个递归定义的数列,通常表示为:
\[ F(n) = F(n-1) + F(n-2) \]
其中,\( F(0) = 0 \) 和 \( F(1) = 1 \)。
前几个Fibonacci数是:
\[ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, \ldots \]
### Fibonacci查找步骤
以下是Fibonacci查找的详细步骤:
1. **准备阶段**:
- 计算出一个足够大的Fibonacci数,使得该数大于或等于数组的长度。这可以通过迭代方法实现。
2. **初始化指针**:
- 初始化三个指针:\( fibMm2 \)(表示\( F(m-2) \)),\( fibMm1 \)(表示\( F(m-1) \)),以及\( fibM \)(表示\( F(m) \))。初始值是\( fibMm2 = 0 \),\( fibMm1 = 1 \),\( fibM = fibMm2 + fibMm1 \)。
3. **搜索阶段**:
- 通过不断调整指针来缩小搜索范围,直到找到目标值或确认目标值不存在。
- 具体过程如下:
- 计算“检查点”位置:\( offset + fibMm2 \)。
- 比较该位置的值与目标值:
- 如果相等,则找到目标值。
- 如果目标值小于该位置的值,则调整指针以缩小搜索范围至左侧部分。
- 如果目标值大于该位置的值,则调整指针以缩小搜索范围至右侧部分。
4. **结束条件**:
- 确定目标值是否存在于数组中。
- 如果搜索范围缩小到单个元素,直接比较该元素与目标值。
### 示例
让我们通过一个示例来更好地理解Fibonacci查找:
假设我们有一个有序数组\[10, 22, 35, 40, 45, 50, 80, 82, 85, 90, 100\],我们要查找目标值85。
1. **准备阶段**:
- 数组长度为11,找到的最小Fibonacci数大于或等于11的是13(即F7 = 13)。
2. **初始化指针**:
- \( fibMm2 = 5 \)(F5 = 5)
- \( fibMm1 = 8 \)(F6 = 8)
- \( fibM = 13 \)(F7 = 13)
3. **搜索阶段**:
- 初始检查点位置:\( offset + fibMm2 = -1 + 5 = 4 \)。
- 数组中第4个位置的值是45,小于85,因此我们缩小搜索范围至右侧部分。
- 更新指针:
- \( fibM = fibMm1 = 8 \)
- \( fibMm1 = fibMm2 = 5 \)
- \( fibMm2 = fibM - fibMm1 = 3 \)
- 新的检查点位置:\( offset + fibMm2 = 4 + 3 = 7 \)。
- 数组中第7个位置的值是82,小于85,因此我们继续缩小搜索范围至右侧部分。
- 更新指针:
- \( fibM = fibMm1 = 5 \)
- \( fibMm1 = fibMm2 = 3 \)
- \( fibMm2 = fibM - fibMm1 = 2 \)
- 新的检查点位置:\( offset + fibMm2 = 7 + 2 = 9 \)。
- 数组中第9个位置的值是85,正好等于目标值,因此查找成功。
### 总结
Fibonacci查找通过利用Fibonacci数列中的数来确定分割点,从而有效地缩小搜索范围。在某些情况下,它比传统的二分查找更具优势。它的实现和理解需要一些数学基础和对Fibonacci数列的熟悉。
下面是Fibonacci查找的Python代码实现以及逐行解释:
```python
# 导入所需的模块
def fibonacci_search(arr, x):
"""
在有序数组arr中查找目标值x,如果找到则返回其索引,否则返回-1
"""
# 初始化Fibonacci数
fibMm2 = 0 # (m-2)'th Fibonacci number
fibMm1 = 1 # (m-1)'th Fibonacci number
fibM = fibMm1 + fibMm2 # m'th Fibonacci number
# 找到一个大于或等于数组长度的Fibonacci数
n = len(arr)
while fibM < n:
fibMm2 = fibMm1
fibMm1 = fibM
fibM = fibMm1 + fibMm2
# 标记被检查的子数组的起始位置
offset = -1
# 当还有元素需要检查时
while fibM > 1:
# 检查位置应该是在offset之后的第fibMm2个位置
i = min(offset + fibMm2, n - 1)
# 如果x大于当前索引的值,向右子数组移动
if arr[i] < x:
fibM = fibMm1
fibMm1 = fibMm2
fibMm2 = fibM - fibMm1
offset = i
# 如果x小于当前索引的值,向左子数组移动
elif arr[i] > x:
fibM = fibMm2
fibMm1 = fibMm1 - fibMm2
fibMm2 = fibM - fibMm1
# 如果找到目标值,返回其索引
else:
return i
# 检查最后一个元素是否是目标值
if fibMm1 and offset < (n - 1) and arr[offset + 1] == x:
return offset + 1
# 如果目标值不存在于数组中,返回-1
return -1
# 示例数组和目标值
arr = [10, 22, 35, 40, 45, 50, 80, 82, 85, 90, 100]
x = 85
# 调用Fibonacci查找函数
result = fibonacci_search(arr, x)
# 打印结果
if result != -1:
print(f"元素 {x} 在数组中的索引为 {result}")
else:
print("元素不在数组中")
```
### 逐行解释
1. **函数定义**:
```python
def fibonacci_search(arr, x):
```
- 定义一个名为`fibonacci_search`的函数,接受一个有序数组`arr`和一个目标值`x`作为参数。
2. **初始化Fibonacci数**:
```python
fibMm2 = 0 # (m-2)'th Fibonacci number
fibMm1 = 1 # (m-1)'th Fibonacci number
fibM = fibMm1 + fibMm2 # m'th Fibonacci number
```
- 初始化三个Fibonacci数:`fibMm2`(F(m-2)),`fibMm1`(F(m-1))和`fibM`(F(m))。
3. **找到大于或等于数组长度的Fibonacci数**:
```python
n = len(arr)
while fibM < n:
fibMm2 = fibMm1
fibMm1 = fibM
fibM = fibMm1 + fibMm2
```
- 通过循环找到一个大于或等于数组长度`n`的Fibonacci数。
4. **初始化偏移量**:
```python
offset = -1
```
- 用于标记被检查的子数组的起始位置。
5. **开始查找**:
```python
while fibM > 1:
i = min(offset + fibMm2, n - 1)
```
- 当还有元素需要检查时,计算检查点位置`i`,它是偏移量加上`fibMm2`,但不能超过数组的长度。
6. **比较当前元素与目标值**:
```python
if arr[i] < x:
fibM = fibMm1
fibMm1 = fibMm2
fibMm2 = fibM - fibMm1
offset = i
```
- 如果当前元素小于目标值,更新Fibonacci数并调整偏移量`offset`,继续向右子数组查找。
```python
elif arr[i] > x:
fibM = fibMm2
fibMm1 = fibMm1 - fibMm2
fibMm2 = fibM - fibMm1
```
- 如果当前元素大于目标值,更新Fibonacci数,继续向左子数组查找。
```python
else:
return i
```
- 如果当前元素等于目标值,返回其索引。
7. **检查最后一个元素**:
```python
if fibMm1 and offset < (n - 1) and arr[offset + 1] == x:
return offset + 1
```
- 检查最后一个元素是否是目标值,如果是,返回其索引。
8. **目标值不在数组中**:
```python
return -1
```
- 如果目标值不存在于数组中,返回-1。
9. **示例数组和目标值**:
```python
arr = [10, 22, 35, 40, 45, 50, 80, 82, 85, 90, 100]
x = 85
```
10. **调用Fibonacci查找函数**:
```python
result = fibonacci_search(arr, x)
```
11. **打印结果**:
```python
if result != -1:
print(f"元素 {x} 在数组中的索引为 {result}")
else:
print("元素不在数组中")
```
- 根据查找结果打印相应的消息。
希望这个解释能帮助你理解Fibonacci查找算法及其实现过程!
### Fibonacci查找的最好情况和平均情况
#### 最好情况
在最好情况下,Fibonacci查找的时间复杂度是 \(O(1)\)。这种情况发生在以下情形之一:
1. **目标值在数组的初始位置**:如果目标值位于数组的第一个位置,我们只需一次比较即可找到目标值。
2. **目标值恰好位于当前检查点**:如果每次计算的检查点位置刚好是目标值所在的位置,我们可以在最少的比较次数内找到目标值。
#### 平均情况
在平均情况下,Fibonacci查找的时间复杂度约为 \(O(\log n)\)。这是因为该算法在每一步都将搜索范围缩小到一个与Fibonacci数有关的子范围,类似于二分查找。
具体分析:
- Fibonacci查找的核心思想是将数组分割成较大和较小的两部分,这两部分的大小近似于连续的Fibonacci数。
- 每次分割后,算法会递归地在较小的子数组中继续查找,这个过程类似于二分查找,但每次分割的位置不是中点,而是基于Fibonacci数。
### 对比二分查找
- **二分查找**:每次都将数组一分为二,分割点总是中间位置。时间复杂度是 \(O(\log n)\)。
- **Fibonacci查找**:每次的分割点是基于Fibonacci数,分割出来的两个子数组的大小比为黄金分割比(大约是1.618:1)。时间复杂度同样为 \(O(\log n)\),但在某些特定情况下(例如某些硬件架构),可能会更高效。
### 总结
- **最好情况**: \(O(1)\),发生在目标值在数组的初始位置或检查点位置。
- **平均情况**: \(O(\log n)\),与二分查找的时间复杂度相同,但在某些特定情况下,Fibonacci查找可能更高效。
Fibonacci查找与二分查找的主要区别在于分割点的选择。虽然它们的平均时间复杂度相同,但在特定场景下,Fibonacci查找可能具有一定优势。
### 学生与老师的课堂讨论:Fibonacci查找的最坏情况分析
#### 学生A:老师,什么是Fibonacci查找啊?🤔
#### 老师:Fibonacci查找是一种结合了Fibonacci数列和二分查找思想的搜索算法。它主要用于在有序向量中查找目标值,比普通的二分查找更高效一些。
#### 学生B:那它和二分查找有什么不同呢?🧐
#### 老师:区别在于分割点的选择。二分查找总是选择中间点,而Fibonacci查找则使用Fibonacci数列中的位置来选择分割点。这样做可以使得查找过程在某些情况下更快。
#### 学生C:那最坏情况下,成功查找和失败查找的比较次数一样吗?🤨
#### 老师:是的,最坏情况下,无论成功还是失败,Fibonacci查找的比较次数都是一样的。我们可以通过几个具体的例子来理解这一点。
### 例子1:查找成功
#### 老师:假设我们有一个有序向量\[1, 3, 7, 15, 31, 63, 127\],我们要查找值31。
#### 学生A:那我们该怎么做呢?
#### 老师:我们使用Fibonacci数列来选择分割点。首先,我们找到小于或等于向量长度的最大Fibonacci数,这里是8(F8 = 21,而F7 = 13,小于等于7)。
#### 学生B:然后呢?
#### 老师:我们用F8 = 21来分割向量,但因为向量长度只有7,所以我们选择位置6(21 - 13 = 8,8 - 1 = 7,超出范围)。我们选择F7 = 13的分割点,也就是位置4(63)。
#### 学生C:63比31大,所以我们在左边继续查找,对吗?🤔
#### 老师:对的。接下来,我们使用F6 = 8(13 - 8 = 5,5 - 1 = 4),选择位置2(7)。
#### 学生A:7比31小,所以我们在右边查找。接下来呢?
#### 老师:我们用F5 = 5(8 - 5 = 3,3 - 1 = 2),选择位置3(15)。
#### 学生B:15比31小,所以我们继续向右查找。剩下的就是位置4(31)。
#### 老师:没错,最后一步,我们找到31,总共进行了4次比较。
### 例子2:查找失败
#### 老师:现在我们查找值64,还是在同样的有序向量\[1, 3, 7, 15, 31, 63, 127\]。
#### 学生C:我们又要用Fibonacci数列分割,对吗?
#### 老师:对的,步骤和之前相同。首先选择位置4(63)。
#### 学生A:63比64小,我们继续向右查找。接下来是位置6(127)。
#### 老师:对,但127比64大,所以我们在左边继续查找,剩下位置5(空)。
#### 学生B:这就失败了,总共也是4次比较。
### 例子3:查找成功边界值
#### 老师:我们再查找一个有趣的值,比如1或127。
#### 学生C:查找1应该和查找31类似吧?我们会选择位置4(63),然后向左查找位置2(7),再向左查找位置1(3),最后找到位置0(1)。
#### 老师:很好,总共也是4次比较。同样地,查找127会向右逐步缩小范围,最终也是4次比较。
### 总结
#### 学生A:所以无论成功还是失败,最坏情况下,Fibonacci查找的比较次数是一样的,对吗?
#### 老师:没错,这就是Fibonacci查找的一个重要特性。通过使用Fibonacci数列,我们可以确保在最坏情况下,比较次数是相同的,从而提供了稳定的性能。
#### 学生B:明白了!这样讲解真的很有帮助!😊
#### 老师:很高兴你们能理解。如果还有其他问题,随时提问哦!
相关文章:
搜索算法:Fibonacci查找
### 什么是Fibonacci查找 Fibonacci查找是一种搜索算法,它结合了Fibonacci数列和二分查找的思想,用于在有序数组中查找目标值。它的主要优点是在某些情况下可以比普通二分查找更高效。 ### Fibonacci数列 Fibonacci数列是一个递归定义的数列࿰…...
软件验收测试报告有什么作用?第三方验收测试报告包括哪些内容?
在现代软件开发中,软件验收测试报告占据了极为重要的地位,不仅是软件交付过程中的一环,更是软件质量保障的关键工具。 软件验收测试报告是指在软件开发过程中,针对软件的功能、性能、安全等方面进行的一系列测试后,形…...
AI大模型教程 Prompt提示词工程 AI原生应用开发零基础入门到实战【2024超细超全,建议收藏】
在AGI(通用人工智能)时代,那些既精通AI技术、又具备编程能力和业务洞察力的复合型人才将成为最宝贵的资源。为此,我们提出了‘AI全栈工程师’这一概念,旨在更精准地描述这一复合型人才群体,而非过分夸大其词…...
Pinia的快捷使用方法
安装Pinia npm install pinia 在main.js里面引入并注册挂载使用 在src下创建一个store inex.js // index.js import { defineStore } from pinia import { computed, ref } from vue //更简洁的的模块化 transferringValuesBetweenComponents simulationModule //简单定义了…...
一文搞懂C++继承
一文搞懂C继承 1.继承的概念及定义1.1继承的概念1.2 继承定义1.2.1定义格式1.2.2继承关系和访问限定符1.2.3继承基类成员访问方式的变化 2.基类和派生类对象赋值转换3.继承中的作用域4.派生类的默认成员函数4.1 构造函数4.2 拷贝构造4.3 赋值重载4.4 析构函数 5.继承与友元6. 继…...
MFC -文件类控件
前言 各位师傅大家好,我是qmx_07,今天给大家讲解MFC中的文件类 MFC文件类 在MFC中,CFILE 是基本的文件操作类,提供了读取、写入、打开、关闭等操作方法主要成员函数:Open(用于打开文件,设置模式 例如 只读 只写 读…...
Hbase操作手册
一:Hbase 创建数据库表 1.进入hbase shell 2.创建数据库表的命令:create 表名, 列族名1,列族名2,列族名N 3.如果想查看所有数据库表,可以使用list 命令: 4.可以看到,刚创建的数据库表user 已经在数据库表的列表中&…...
vue组件($refs对象,动态组件,插槽,自定义指令)
一、ref 1.ref引用 每个vue组件实例上,都包含一个$refs对象,里面存储着对应dom元素或组件的引用。默认情况下,组件的$refs指向一个空对象。 2.使用ref获取dom元素的引用 <template><h3 ref"myh3">ref组件</h3&g…...
构建高可用和高防御力的云服务架构第五部分:PolarDB(5/5)
引言 云计算与数据库服务 云计算作为一种革命性的技术,已经深刻改变了信息技术行业的面貌。它通过提供按需分配的计算资源,使得数据存储、处理和分析变得更加灵活和高效。在云计算的众多服务中,数据库服务扮演着核心角色。数据库服务不仅负…...
QT窗口无法激活弹出问题排查记录
问题背景 问题环境 操作系统: 银河麒麟V10SP1qt版本 : 5.12.12 碰见了一个问题应用最小化,然后激活程序窗口无法弹出 这里描述一下代码的逻辑,使用QLocalServer实现一个单例进程,具体的功能就是在已存在一个程序A进程时,再启动这个程序A,新的程序A进程会被杀死,然后激活已存…...
node.js 版本管理
在Node.js开发中,版本管理是一个非常重要的环节,特别是当你需要同时维护多个项目,而这些项目又依赖于不同版本的Node.js时。以下是一些常用的Node.js版本管理工具和方法: 1. NVM (Node Version Manager) NVM是Node.js版本管理的…...
使用Python实现图形学曲线和曲面的NURBS算法
目录 使用Python实现图形学曲线和曲面的NURBS算法引言NURBS曲线的数学原理1. NURBS曲线定义2. 权重的作用 NURBS曲线的Python实现1. 类结构设计2. 代码实现3. 代码详解使用示例 NURBS曲面的扩展NURBS曲面类实现 总结 使用Python实现图形学曲线和曲面的NURBS算法 引言 NURBS&a…...
SpringBoot3
文章目录 一、为什么要学习SpringBoot二、SpringBoot介绍2.1 约定优于配置2.2 SpringBoot中的约定三、SpringBoot快速入门3.1 快速构建SpringBoot3.1.1 选择构建项目的类型3.1.2 项目的描述3.1.3 指定SpringBoot版本和需要的依赖3.1.4 导入依赖3.1.5 编写了Controller3.1.6 测试…...
【Text2SQL】领域优质论文分享
解读论文:Enhancing Few-shot Text-to-SQL Capabilities of Large Language Models: A Study on Prompt Design Strategies 1. 重要贡献 这篇论文的主要贡献在于提出了一种新的方法来增强大型语言模型(LLMs)在少量样本(Few-shot…...
2024全国研究生数学建模竞赛(数学建模研赛)ABCDEF题深度建模+全解全析+完整文章
全国研究生数学建模竞赛(数学建模研赛)于9月21日8时正式开赛,赛程4天半,咱这边会在开赛后第一时间给出对今年的6道赛题的评价、分析和解答。包括ABCDEF题深度建模全解全析完整文章,详情可以点击底部的卡片来获取哦。 …...
Java项目中异常处理的最佳实践
1. 异常分类 首先,理解异常的不同类型是合理处理异常的基础。Java中的异常大致可以分为两大类: 受检异常(Checked Exceptions):这些异常必须被捕获或声明抛出,例如IOException。非受检异常(Un…...
CSS基本概念以及CSS的多种引入方式
CSS基本概念 CSS是层叠样式表,又叫级联样式表,简称样式表。CSS的文件后缀为.css,CSS用于HTML文档中元素样式的定义。 CSS的基本语法 CSS的规则由2个主要的部分构成:选择器以及一条或者多条声明。 选测器通常是你血药改变样式的…...
TiDB 简单集群部署拓扑文件
TiDB集群部署 服务器环境部署拓扑 都2024了还在为分库分表烦恼吗😘,用分布式数据库TiDB、OceanBase、华为 GaussDB,你就使劲往里存数据。 早下班、少脱发、脱单! 🙏🏻🙏🏻Ƕ…...
十三 系统架构设计(考点篇)
1 软件架构的概念 一个程序和计算系统软件体系结构是指系统的一个或者多个结构。结构中包括软件的构件,构件 的外部可见属性以及它们之间的相互关系。 体系结构并非可运行软件。确切地说,它是一种表达,使软件工程师能够: (1)分…...
Java-数据结构-二叉树-习题(三)  ̄へ ̄
文本目录: ❄️一、习题一(前序遍历非递归): ▶ 思路: ▶ 代码: ❄️二、习题二(中序遍历非递归): ▶ 思路: ▶ 代码: ❄️三、习题三(后序遍历非递归): ▶ 思路: …...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
