python 斐波那契搜索-迭代与递归(Fibonacci Search)

本文详细介绍了斐波那契搜索算法,一种基于比较的搜索技术,用于在排序数组中高效查找元素。它利用斐波那契数进行分割,具有O(logn)的时间复杂度,与二分查找类似但有特定区别。文章通过示例和代码展示了算法的工作原理,并讨论了其性能优劣和适用场景。
摘要由CSDN通过智能技术生成

        给定一个大小为 n 的排序数组 arr[] 和要在其中搜索的元素 x。如果 x 存在于数组中,则返回 x 的索引,否则返回 -1。 
例子: 
输入:  arr[] = {2, 3, 4, 10, 40}, x = 10
输出:  3
元素 x 出现在索引 3 处。

输入:  arr[] = {2, 3, 4, 10, 40}, x = 11
输出:  -1
元素 x 不存在。

        斐波那契搜索是一种基于比较的技术,它使用斐波那契数来搜索排序数组中的元素。
与二分查找的相似之处:
        1、适用于排序数组
        2、分而治之的算法。
        3、具有 Log n 时间复杂度。
与二分查找的区别:
        1、斐波那契搜索将给定数组分成不相等的部分
        2、二分查找使用除法运算符来划分范围。斐波那契搜索不使用 /,而是使用 + 和 -。在某些 CPU 上,除法运算符的成本可能很高。
        3、斐波那契搜索在后续步骤中检查相对较近的元素。因此,当输入数组很大,无法容纳 CPU 缓存甚至 RAM 时,斐波那契搜索可能会很有用。
背景: 
        斐波那契数被递归地定义为 F(n) = F(n-1) + F(n-2)、F(0) = 0、F(1) = 1。前几个斐波那契数是 0、1、 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
观察: 
下面的观察用于范围消除,因此用于 O(log(n)) 复杂度。  
F(n - 2) ≈ (1/3)*F(n) 且
F(n - 1) ≈ (2/3)*F(n)。
算法: 
设要查找的元素为x。
        这个想法是首先找到大于或等于给定数组长度的最小斐波那契数。令找到的斐波那契数为 fib(第 m 个斐波那契数)。我们使用第 (m-2) 个斐波那契数作为索引(如果它是有效索引)。设第(m-2)个斐波那契数为i,我们将arr[i]与x进行比较,如果x相同,则返回i。否则,如果 x 更大,则对 i 之后的子数组进行递归,否则对 i 之前的子数组进行递归。
下面是完整的算法 
设 arr[0..n-1] 为输入数组,要搜索的元素为 x。
        1、找到大于或等于 n 的最小斐波那契数。令这个数字为 fibM [第 m 个斐波那契数]。令其前面的两个斐波那契数为 fibMm1 [(m-1) 个斐波那契数] 和 fibMm2 [(m-2) 个斐波那契数]。
        2、当数组有要检查的元素时: 
                2.1、将 x 与 fibMm2 覆盖范围的最后一个元素进行比较
                2.2、如果x 匹配,则返回索引
                2.3、否则,如果x 小于该元素,则将三个斐波那契变量向下移动两个斐波那契,表示消除剩余数组的大约后三分之二。
                2.4、否则x 大于该元素,将三个斐波那契变量向下移动一位斐波那契。重置索引的偏移量。这些一起表明剩余阵列的大约前三分之一被消除。
        3、由于可能还剩下一个元素可供比较,因此检查 fibMm1 是否为 1。如果是,则将 x 与该剩余元素进行比较。如果匹配,则返回索引。 

# Python3 program for Fibonacci search. 
from bisect import bisect_left 
  
# Returns index of x if present,  else 
# returns -1 
  
  
def fibMonaccianSearch(arr, x, n): 
  
    # Initialize fibonacci numbers 
    fibMMm2 = 0  # (m-2)'th Fibonacci No. 
    fibMMm1 = 1  # (m-1)'th Fibonacci No. 
    fibM = fibMMm2 + fibMMm1  # m'th Fibonacci 
  
    # fibM is going to store the smallest 
    # Fibonacci Number greater than or equal to n 
    while (fibM < n): 
        fibMMm2 = fibMMm1 
        fibMMm1 = fibM 
        fibM = fibMMm2 + fibMMm1 
  
    # Marks the eliminated range from front 
    offset = -1
  
    # while there are elements to be inspected. 
    # Note that we compare arr[fibMm2] with x. 
    # When fibM becomes 1, fibMm2 becomes 0 
    while (fibM > 1): 
  
        # Check if fibMm2 is a valid location 
        i = min(offset+fibMMm2, n-1) 
  
        # If x is greater than the value at 
        # index fibMm2, cut the subarray array 
        # from offset to i 
        if (arr[i] < x): 
            fibM = fibMMm1 
            fibMMm1 = fibMMm2 
            fibMMm2 = fibM - fibMMm1 
            offset = i 
  
        # If x is less than the value at 
        # index fibMm2, cut the subarray 
        # after i+1 
        elif (arr[i] > x): 
            fibM = fibMMm2 
            fibMMm1 = fibMMm1 - fibMMm2 
            fibMMm2 = fibM - fibMMm1 
  
        # element found. return index 
        else: 
            return i 
  
    # comparing the last element with x */ 
    if(fibMMm1 and arr[n-1] == x): 
        return n-1
  
    # element not found. return -1 
    return -1
  
  
# Driver Code 
arr = [10, 22, 35, 40, 45, 50, 
       80, 82, 85, 90, 100,235] 
n = len(arr) 
x = 235
ind = fibMonaccianSearch(arr, x, n) 
if ind>=0: 
  print("Found at index:",ind) 
else: 
  print(x,"isn't present in the array"); 
  
# This code is contributed by rishabh_jain  

输出
发现于索引:11
插图: 
让我们通过下面的例子来理解算法:  

示例假设:基于 1 的索引。目标元素 x 为 85。数组长度 n = 11。
        大于或等于 11 的最小斐波那契数为 13。根据我们的插图,fibMm2 = 5、fibMm1 = 8 和 fibM = 13。
        另一个实现细节是偏移变量(零初始化)。它标志着已被淘汰的范围,从前面开始。我们会不时更新。
        现在,由于偏移值是一个索引,并且包括它及其以下的所有索引都已被消除,因此只有向其添加一些内容才有意义。由于 fibMm2 标记了大约三分之一的数组,并且它标记的索引肯定是有效的,因此我们可以将 fibMm2 添加到 offset 并检查索引 i = min(offset + fibMm2, n) 处的元素。 

可视化: 

时间复杂度分析: 
当我们继续寻找目标时,当我们的目标位于数组的较大 (2/3) 部分时,最坏的情况就会发生。换句话说,我们每次都会消除数组中较小的 (1/3) 部分。我们对 n 调用一次,然后对 (2/3) n 调用一次,然后对 (4/9) n 调用一次,以此类推。
考虑一下:  

辅助空间: O(1)

方法 2:迭代
        斐波那契搜索是一种搜索算法,用于查找排序数组中元素的位置。斐波那契搜索的基本思想是使用斐波那契数来确定数组中的分割点,并对适当的子数组进行二分搜索。
下面是使用迭代方法的斐波那契搜索的 Python 实现: 

def fibonacci_search(arr, x): 
    n = len(arr) 
    if n == 0: 
        return -1
  
    # Initialize Fibonacci numbers 
    fib1, fib2 = 0, 1
    fib3 = fib1 + fib2 
  
    # Find the smallest Fibonacci number greater than or equal to n 
    while fib3 < n: 
        fib1, fib2 = fib2, fib3 
        fib3 = fib1 + fib2 
  
    # Initialize variables for the current and previous split points 
    offset = -1
    while fib3 > 1: 
        i = min(offset + fib2, n-1) 
  
        # If x is greater than the value at index i, move the split point to the right 
        if arr[i] < x: 
            fib3 = fib2 
            fib2 = fib1 
            fib1 = fib3 - fib2 
            offset = i 
  
        # If x is less than the value at index i, move the split point to the left 
        elif arr[i] > x: 
            fib3 = fib1 
            fib2 = fib2 - fib1 
            fib1 = fib3 - fib2 
  
        # If x is equal to the value at index i, return the index 
        else: 
            return i 
  
    # If x is not found in the array, return -1 
    if fib2 == 1 and arr[offset+1] == x: 
        return offset + 1
    else: 
        return -1
  
  
  
  
# Driver Code 
arr = [10, 22, 35, 40, 45, 50, 
    80, 82, 85, 90, 100,235] 
n = len(arr) 
x = 235
ind = fibonacci_search(arr, x) 
if ind>=0: 
    print("Found at index:",ind) 
else: 
    print(x,"isn't present in the array"); 
  
# This code is contributed by ajay singh 

输出
发现于索引:11
        斐波那契搜索的时间复杂度为 O(log n),其中 n 是输入数组的长度。
        这是因为在算法的每次迭代中,搜索范围都会减少约 1/?,其中 ?是黄金比例(? ? 1.618)。将搜索范围缩小到单个元素所需的迭代次数大约为 log?(n),其中 log?表示自然对数。
        由于斐波那契搜索的每次迭代都需要恒定的时间,因此算法的总体时间复杂度为 O(log n)。这使得斐波那契搜索成为比线性搜索更快的算法,但比二分搜索和其他对数搜索算法(例如插值搜索和指数搜索)慢。
辅助空间:O(1)  

  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值