我所知道查找算法之斐波拉契(黄金分割法)查找

作者前言

大家好,我是阿濠,今篇内容跟大家分享的是查找算法之斐波那契(黄金分割法)查找,很高兴分享到segmentfault与大家一起学习交流,初次见面请大家多多关照,一起学习进步.

一、斐波那契数列介绍

clipboard.png

被我们称为"斐波拉契"的人,真实姓名叫列昂纳多来自比萨,这个数列出自他的书《算盘宝典》("Liber Abaci"),这本书奠定西方世界的数学基础,其中的算法方法一直沿用至今

什么是斐波那契数列?

斐波那契数列指的是这样一个数列: 0, 1, 1, 2, 3, 5, 8, 13, 21....

特别指出:第0项是0,第1项是第一个1

这个数列从第三项开始,每一项都等于前两项之和

斐波那契公式:F(k)=F(k-1)+F(k-2) 提示:F(1)=1 F(2)=1

初识美丽漂亮的黄金分割

黄金分割点是把一条线段分割为两部分,其中一部分与全长之比等于另一部分与这部分之比

图片.png

由于按此比例设计的造型十分美丽,因此称为黄金分割,也称为中外比。

clipboard.png
clipboard.png

取其前三位数字的近似值是0.618,这是一个神奇的数字,会带来意向不大的效果

如果用大的斐波那契数 / 小的斐波那契数,也会发现越来越接近0.618

图片.png

二、斐波那契(黄金分割法)查找算法介绍

基本原理

斐波那契查找原理与前两种相似,仅仅改变中间结点(mid)的位置mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1 (F代表斐波那契数列)

图片.png

对F(k-1)-1的理解:

1.根据公式: F[k] = F[k-1] + F[k-2],得到(F[k]-1) = (F[k-1]-1) + (F[k-2]-1) +1

2.因为有时候顺序表长度 n 不一定刚好等于 F[k]-1 ,将原数组查找表扩展为长度为F[k]-1 (如果要补充元素,则补充重复最后一个元素直到满足F[k]-1个元素),完成后进行斐波那契分割

3.如图所示,只要顺序表的长度为 F[k]-1 就可以分割为前半部分F[k-1]-1]个元素,后半部分 F[k-2]-1 个元素,从而确定中间位置 mid = low+F(k-1)-1,找出要查找的元素在那一部分并递归,直到找到。

为什么(F[k]-1) = (F[k-1]-1) + (F[k-2]-1) +1 ?

因为根据公式得出:F[k] = F[k-1] + F[k-2],而斐波那契数列是指:{1,1,2,3,5,8, 13...}
若此时 k = 4 ,则代入公式:F[4] = F[4-1] + F[4-2]求出数列F[4] 结果:5
若此时代入(F[k]-1),那么本来 求F[4] 就变成求 (F[4]-1) 结果就是f[4] - 1 = 5 - 1 = 4
公式代入则是:(F[4]-1) = (F[4-1]-1) + (F[4-2]-1) 也就是(F[4]-1) = (F[3]-1) + (F[2]-1)
摊开来将F[4]=5、F[3]=3、F[2]=1 代入:(5-1) = (3-1) + (2-1) 此时再 + 1 就相等了

三、通过应用示例认识斐波那契查找算法

有序数组arr={1,8,10,89,1000,1234},进行斐波那契查找输入一数看看该数组是否存在此数,存在则求出下标,如果没有就返回-1 表示没有这个数

//因为后面我们mid=low+F(k-1)-1,需要使用到斐波那契数列
//因此我们需要先获取到一个斐波那契数列
//非递归方法得到一个斐波那契数列
public static int[] fib() {
    int[] f = new int[maxSize];
    f[0] = 1;
    f[1] = 1;
    for(int i=2;i<maxSize;i++){
        f[i] = f[i- 1] + f[i- 2];
    }
    return f;
}

//使用非递归实现
/**
 * @param arr 数组
 * @param key 我们要查找的关键值(码)
 * @return 返回对应的下标 如果没有返回-1
 */
public static  int fibSearch(int [] arr,int key) {

    int low = 0;
    int hight = arr.length - 1;
    int k = 0;//表示斐波那契分割数值下标
    int mid = 0;
    int f[] = fib();//获取斐波那契数列


    //判断顺序表长度 n 是否等于 F [k]-1 `,不等于将`原数组查找表扩展为长度为F[k]-1
    //假设当前传入的数组:1, 8, 10, 89, 1000, 1234  hight = 5
    //斐波那契数列:1,1,2,3,5,8,13...
    while (hight > f[k] - 1) {
        k++;
    }

    //如果要补充元素,则补充重复最后一个元素,直到满足F[k]-1个元素
    int[] temp = Arrays.copyOf(arr, f[k]);//此时 k =5  f[5]=8

    //因为arr[hight]代表最后一个元素,新数组temp = Arrays.copyOf(arr,f[k]);
    //所以若想最后元素当作填充元素就应该是从hight + 1 开始
    for (int i = hight + 1; i < temp.length; i++) {
        temp[i] = arr[hight];
    }

    while (low <= hight) {

        //按图所示进行黄金分割前部分+后部分
        mid = low + f[k - 1] - 1;

        //如果需要找的值key 小于temp[mid]说明我们应该继续向数组的前面查找(左边)
        if (key < temp[mid]) {

            hight = mid - 1;//往前缩范围

            //为什么是k--
            //1.全部元素= 前面的元素 + 后边元素
            //2. f[k] = f[k-1] + f[k-2]
            //之前(F[k]-1) = (F[k-1]-1) + (F[k-2]-1) +1
            //按图所示,目前我们这里是mid= low + f[k - 1] -1;
            //如果继续向数组的前面查找(左边)则应该是(F[k-1]-1)进行拆分
            //(F[k-1]-1)=(F[k-1-1]-1) + (F[k-2-2]-1) +1 = 4 = 2 + 1 + 1
            //即在f[k-1]-1 的前面继续查找k--
            //即下次循环mid = f[k-1-1] -1
            k--;
        }

        //我们应该继续向数组的后面查找(右边)
        if (key > temp[mid]) {
            low = mid + 1;
            //为什么是k -=2
            //1. f[k] = f[k-1] + f[k-2] .
            //2.因为后面我们有f[k-2],所以可以继续拆分 f[k-1] = f[k-3] + f[k-4]
            //3.即在f[k-2]的前面进行查找k -=2,即下次循环mid = f[k -1 - 2] -1
            k -= 2;
        }

        if (key == temp[mid]) {

            //因为之前如果要补充元素,则补充重复最后一个元素,直到满足F[k]-1个元素
            //如果小于hight代表是arr数组里的值
            if (mid <= hight) {
                return mid;
            } else {
                //否则说明查找得到的数据元素是temp数组里的补充值
                return hight;
            }
        }

    }
    return -1;
}

如果存在则求出下标,如果没有就返回-1表示没有这个数

执行代码测试一下数据看看,点击这里运行代码

四、算法复杂度分析

斐波那契查找的时间复杂度是:O(log 2 n )

二分法折半查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,而除法比加减法要占用更多的时间,因此,斐波那契查找的运行时间理论上比折半查找小,但是具体还是得视具体情况而定

五、斐波那契数列的有趣知识

计算中的斐波那契数列

图片.png

让我们计算一下,头几个斐波那契数列的平方

图片.png

毫无意外的,当你加上两个连续斐波那契的数字时,你会得到下一个斐波那契数,但是也许你不知道把斐波那契数的平方加起来会有什么有意思的结果?

图片.png

没错,规律还在~事实上,还有一个规律,你计算一下头几个斐波那契数列的平方和

图片.png

可能觉得它们不是斐波那契数,但是如果你看的够仔细,会发现背后隐藏着斐波那契数

图片.png

那么你就会发现1 、1 、2 、3 、5 、8的各平方加起来 = 104 = 8x13 why?

图片.png

用一个简单的图形解答一下,先让我们画一个1 乘 1 的方块

图片.png

图片.png

图片.png

现在问大家一个问题:这个矩形的面积是多少?

图片.png

一方面它的面积是:组成它的小矩形之和

图片.png

一方面因为是矩形,它的面积:长 * 高

图片.png

所以这就是为什么1 、1 、2 、3 、5 、8的各平方加起来 = 104 = 8x13

黄金矩形与黄金螺旋

通过上面的知识点了解了黄金分割线,那么我们来再了解一下黄金矩形与黄金螺旋

图片.png

图片.png

图片.png

图片.png

生活中的斐波那契

斐波那契数列在自然界中神奇的出现,一朵花的花瓣数量、向日葵的螺旋,菠萝上表面的凸起,一般都对应着某个斐波那契数列

clipboard.png

图片.png

图片.png

图片.png

简单的说,植物的生长点每个一个角度就会发展出一个侧芽,如果这个侧芽角度太过平庸,新芽旋转几周后之后就会与老的侧芽对在一起,即浪费空间又争夺资源

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

图片.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值