搜索算法: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-数据结构-二叉树-习题(三)  ̄へ ̄
文本目录: ❄️一、习题一(前序遍历非递归): ▶ 思路: ▶ 代码: ❄️二、习题二(中序遍历非递归): ▶ 思路: ▶ 代码: ❄️三、习题三(后序遍历非递归): ▶ 思路: …...

SpringBoot+Aop+注解方式 实现多数据源动态切换
整体思路: 引入基本依赖SpringBootAopMySqlMyBatislombok在配置文件中配置多个数据源创建数据源配置类用于读取配置编写用于标识切换数据源的注解创建数据源切换工具类DataSourceContextHolder编写切面类用于在注解生效处切换数据源编写配置类,加载数据…...

企业如何高效应对多类型知识产权事务的复杂挑战?
随着企业的发展和创新活动的不断推进,越来越多的企业拥有了大量的专利、商标和软著等知识产权,这些不仅关乎企业的技术创新成果,更直接影响到企业的品牌价值和市场竞争力。然而,当企业拥有多件知识产权时,复杂的申请、…...

openeuler22.03 LTS 源码编译安装nginx1.22.1
openeuler22.03 LTS 源码编译安装nginx1.22.1 下载安装包 #官网下载nginx1.22.1 wget http://nginx.org/download/nginx-1.22.1.tar.gz安装依赖包 #安装依赖包,NGINX是C语言写的,pcre-devel支持正则表达式,openssl 开启加密 [rootproxy ~]…...

图片压缩工具免费怎么找?归纳了这几个压缩工具
有哪些图片压缩工具免费?在数字化时代,图像已成为我们生活中不可或缺的一部分。无论是网站设计、社交媒体分享还是文件传输,高质量的图片都扮演着重要的角色。但高质量往往意味着大文件体积,这可能会导致加载速度变慢或存储空间不…...

【Kubernetes知识点】解读HPA的 thrashing(抖动)问题
【Kubernetes知识点】解读HPA的 thrashing(抖动)问题 目录 1 概念 1.1 什么是 Thrashing 现象?1.2 HPA 中 Thrashing 产生的原因1.3 解决 Thrashing 的优化措施 1.3.1 设置合适的阈值1.3.2 使用自定义指标和基于负载的自动扩缩1.3.3 增加扩…...

Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】 目录 Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】 一、简单介绍 二、装饰者模式(Decorator Pattern) 1、什么时候使用装…...

Linux上Qt安装相关的内容及在QtCreator使用QChart模块需要的配置
引言 下面是Ubuntu上Qt安装相关的内容及在QtCreator使用QChart模块需要的配置。 关于Qt安装及环境 Qt的模块 查看已经安装的模块 sudo apt search qt5-安装新的模块 sudo apt install qt5-svg # 安装Qt SVG模块3.查看qt已经安装了哪些模块 dpkg -l | grep libqt安装qt,…...

lettuce引起的Redis command timeout异常
项目使用Lettuce,在自己的环境下跑是没有问题的。在给客户做售前压测时,因为客户端环境比较恶劣,service服务和中间件服务不在同一机房。服务启动后不一会就会出现Redis command timeout异常。 经过差不多两周的追查,最后没办法把…...

【Hadoop】一、Hadoop入门:基础配置、集群配置、常用脚本
基础设置 网络设置 创建好一个 centos 虚拟机,修改网络配置文件: /etc/sysconfig/network-scripts/ifcfg-ens33修改 BOOTPROTO 为 static 以及添加 IPADDR、GATEWAY、DNS1 TYPE"Ethernet" PROXY_METHOD"none" BROWSER_ONLY&quo…...

Ollama:本地运行大模型【含UI界面】
文章目录 Ollama 简介安装 ollamaWindows 安装Docker 安装其它平台安装支持的模型模型清单模型参数与运行内存快速启动 llama 模型llama 模型介绍运行 llama3.1 模型通过 HTTP API 访问ollama 命令语法常用示例特别示例自定义模型创建 Modelfile创建模型并运行集成 Web 页面Ope…...