查找算法入门教程-黄金分割查找法(斐波拉契)

前面我们学习了常见查找算法的插值和二分查找,这节我们来学习黄金分割查找法,也称斐波拉契,想必大家对斐波拉契函数f(k) = f(k-1) +f(k -2)都知到,我们的黄金分割法用到了斐波拉契函数,接下来我们一起来学习

黄金分割法介绍

黄金分割是指将一条线段分割成两部分,使其中一部分与全长之比等于另一部分与这部分之比,取其全三位的数字的近似值接近0.618,因此该点称为黄金分割点.

斐波拉契数列

如{1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144},我们会发现斐波拉契数列的每相邻两个数的比例是接近0.618,存在的函数为f(k) = f(k-1) +f(k -2),我们来看看黄金分割的原理

黄金分割原理

黄金分割查找的过程是跟二分和插值是类似的,唯一的区别是改变mid(中间值)的位置,对于黄金分割而言,mid不在是通过二分查找和插值查找那样计算得到的,而是遵循这样一个函数mid = F(k -1) -1;也就是我们的mid是接近于黄金分割点的附近,其中F代表为斐波拉契数列,我们来看张图:

斐波拉契数列.png

简单的来介绍下图中的所涉及到的变量
其中 low表示: 数列的左边索引也就是(left)
high表示:数列的右边索引也就是(right)

接着我们来理解下mid = F(k -1) -1过程

  • 我们都知道斐波拉契数列遵循f(k) = f(k-1) +f(k -2)性质,因此我们可以得到(F[k] -1) = (F[k -1] -1) +(F[k-2] -1),这个表达式告诉我们的有序表长度为F[k] -1时,可以将它分成F[K-1] -1和F[k-2] -1这两部分,也就是图中所示的一样,这样的话我们的mid = low +F[k-1] -1.
  • 我们可以类比上面所述,每一个子段也可以采用此过程进行分割的
  • 我们需要考虑到一个点,我们的有序列表的长度size可能不一定刚好等于F[k] -1;这个时候就需要考虑到增加原来的有序列表长度由size增至F[k] -1. 注意:k的值要能让F[k] -1的值恰好大于等于size即可,当然原有序列表的长度是从size+1处到F[k] -1的位置上都填充原有序列表的为size处的数字即可.

说了这么多,很难理解,我们通过代码来实现,假设我的有序列表为int[] arr = {1,8,10,89,1000,1234},来看代码:

代码实现

我们都知道黄金分割借助于斐波拉契来实现的首先我们来初始一组斐波拉契数列

static int maxSize = 20; //斐波拉契数列默认大小

   //创建一个斐波拉契数列 mid = left +F(k-1) -1;
public static int[] fiBo(){
    int[] fi = new int[maxSize];
    fi[0] = 1;
    fi[1] = 1;
    for (int i = 2; i <maxSize ; i++) {
        fi[i] = fi[i -1] + fi[i -2];
    }
    System.out.println(Arrays.toString(fi));
    return fi;
}
  • 测试结果如下图


    测试结果.png

我们的第一步完成了,接下来我看查找的过程,代码如下:

/**
 *
 * @param arr 待查找的数组
 * @param findVal 要查找的数
 * @return 找到返回下标,没找到返回-1
 */
public static int fibSearch(int[] arr,int findVal){
    //变量的定义
    int left = 0; //左边索引
    int right =arr.length -1; //右边索引
    int k = 0; //斐波拉契分割数值的下标
    int mid = 0; //存放找到的mid值
    int[] f = fiBo(); //获取斐波拉契数列
    //循环处理来查找斐波拉契分割数值的下标所对应的值
    while (right > f[k] -1){
        k++;
    }
    //可能存在f[k]的值大于了arr的长度,我们需要扩容,并指向数组temp[]
    //不足的部分使用0来补齐
    int[] temp = Arrays.copyOf(arr, f[k]);
    //我们使用arr数组的最后的数来填充temp数组
    //如:temp={1,8,10,89,1000,1234,0,0,0}====>{1,8,10,89,1000,1234,1234,1234,1234}
    //right +1 = 1234
    for (int i = right +1; i <temp.length ; i++) {
        temp[i] = arr[right];
    }
    //来找我们的findVal,循环处理
    while (left <= right){
        mid = left + f[k -1] -1;
        if (findVal < temp[mid]){ //我们应该在数组的左边继续找
            right = mid -1;
            //说明:
            //1.我们数组的全部元数 = left左边 +right元素
            //2.因为我们的黄金分割点是 f[k] = f[k -1] + f[k -2]
            //3. 可以发现的,当前左边还有f[k -1]个元素,因此可以继续拆分f[k -1] = f[k -2] + f[k -3]等
            // 也就是说在f[k -1]的左边继续查找,即 k-- 即 mid = f[k - 1 -1] -1
            k --;
        }else if (findVal > temp[mid]){ //我们应该在数组的右边继续找
            left = mid +1;
            //说明:
            //1.我们数组的全部元数 = left左边 +right元素
            //2.因为我们的黄金分割点是 f[k] = f[k -1] + f[k -2]
            //3.可以发现的,当前右边还有f[k -2]个元素,因此可以继续拆分f[k -1] = f[k -3] + f[k -4]等
            //即在f[k -2]的右边继续查找 k -=2个元素,即 mid = f[k -1 -2] -1
            k -= 2;
        }else { //表示找到了
            //比较下,我们返回的最小的那个
            if (mid <= right){
                return mid;
            }
            return right;
        }
    }
    return -1;
}

这就是查找核心代码,注释很详细,再加上上面我们文字的解释,结合代码相信大家能明白,我们来看测试代码:

public static void main(String[] args) {

    int[] arr = {1,8,10,89,1000,1234};

    int indexResult = fibSearch(arr, 1234);
    System.out.println(indexResult);

}
  • 测试结果如下图:


    斐波拉契测试结果.png

结果证明代码是没问题,那么关于黄金分割的学习就到这了...

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容