黄金分隔点是指 把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数的近似值是0.618。
斐波那契数列:{1,1,2,3,5,8,13,21,34,55……}发现斐波那契数列的两个相邻数的比例,无限接近黄金分隔值0.618
思路
斐波那契查找的原理与二分查找和插值查找相似,仅仅改变了中间节点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近
即 mid=low+F(k-1)-1
对F(k-1)-1的理解:
- 由于斐波那契数列
F[k]=F[k-1]+[k-2](当前数等于前面两个数的和)的性质,可以得到(F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1
该式说明,只要数组的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1和F[k-2]-1的两段。注:上面式子尾部的+1,表示数组的length,length要不最大索引+1
- 类似的每一段都可以用同样的方式分割
但是顺序表的长度n不一定刚好等于
F[k]-1,所以需要将原来的顺序表长度增加至F[k]-1。这里的k值只要能使得F[k]-1恰好大于或者等于n即可。又下面的代码获得k值。顺序表长度增加后,新增的位置,从n+1到F[k-1]的位置,都赋为n位置的值即可。while(n>fib(k)-1)k++;
由下图所示,从而中间位置为
mid=low+F(k-1)-1

namespace ConsoleApp1 { class Program { static int maxFSize = 20;//斐波那契数组的大小 static void Main(string[] args) { int[] arr = new int[8] { 1, 2, 4, 6, 7, 8, 9, 13 }; Console.WriteLine(FibonacciSearch(arr,9)); } //因为mid=low+F(k-1)-1,需要使用到斐波那契数列,因此我们需要先获取到一个斐波那契数列 static int[] fib() { int[] f = new int[maxFSize]; f[0] = 1; f[1] = 1; for (int i = 2; i < maxFSize; i++) { f[i] = f[i - 1] + f[i - 2]; } return f; } //斐波那契查找 static int FibonacciSearch(int[] array,int finval) { int low = 0; int high = array.Length - 1; int k = 0;//表示斐波那契分割数值的下标 int mid = 0; int[] f = fib();//获取到斐波那契数列
//获取到斐波那契分隔数值的下标while (high > f[k] - 1)k++;//f[k]对应的值不一定和array.Length相同,要保证数组的长度正好小于f[k]-1,要把数组的上限增加到f[k]int[] temp=new int[f[k]];//声明一个新的数组,长度为f[k]Array.Copy(array, temp, array.Length);//把原来的数组拷贝到临时数组里面去,多余的位置都是0//实际需要使用array最后的数填充到temp里面的后面的空位上for (int i = high + 1; i < temp.Length; i++)temp[i] = array[high];//使用while来循环处理,找到findvalwhile (low <= high)//只要这个条件满足就可以一直找{mid = low + f[k - 1] - 1;//找到黄金分隔点if (finval < temp[mid])//说明要继续向mid的前面查找{high = mid - 1;/*说明:*已知斐波那契数列f[k]=f[k-1]+f[k-2]*mid就是分隔这两个部分的,我们现在要往前继续查找,前面部分就是f[k-1]*又因为f[k-1]=f[k-2]+f[k-3]*所要找到mid就要用f[k-2]*再所以这里k--后,再次循环k-1,等价于k-2*/k--;}else if (finval > temp[mid])//说明要继续向mid的后面查找{low = mid + 1;/*说明:* 已知f[k]=f[k-1]+f[k-2]* 现在往后查找,后面部分就是f[k-2]* 又因为f[k-2]=f[k-3]+f[k-4]* 所以要找到mid需要用到f[k-3]* 再所以,这里k-=2后,再次循环k-1,等价于k-3*/k-=2;}else//找到了{//需要确定返回的是哪个下标//high是array的最大下标//mid是temp的黄金分隔点//temp后面还有一些填充的占位数据,要返回的下标一定是在集合[0,array,length-1]里面,而不是在[0,temp.length-1]里面if (mid <= high)return mid;elsereturn high;}}//没有找到就返回-1return -1;}}
}
```
