斐波那契法(Fibonacci method)又称斐波那契分数法,是一种一维搜索的区间消去法(区间消去法(interval elimination method)求单变量函数无约束极值的较实用的一类直接搜索方法.其特点是在搜索过程中,不断缩小最优点所在的 区间,即通过搜索区间的逐步缩小来确定最优点.)。 【另外】:优化方法除了区间消去法,还有进退法。
斐波那契法(Faboncci Method)
什么是斐波那契查找
斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、····,在数学上,斐波那契被递归方法如下定义:F(1)=1,F(2)=1,F(n)=f(n-1)+F(n-2) (n>=2)。该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)。
斐波那契查找就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F(n),将原查找表扩展为长度为F(n)(如果要补充元素,则补充重复最后一个元素,直到满足F(n)个元素),完成后进行斐波那契分割,即F(n)个元素分割为前半部分F(n-1)个元素,后半部分F(n-2)个元素,找出要查找的元素在那一部分并递归,直到找到。
斐波那契查找的时间复杂度还是O(log 2 n ),但是与二分查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,而除法比加减法要占用更多的时间,因此,斐波那契查找的运行时间理论上比二分查找小,但是还是得视具体情况而定。
对于斐波那契数列:1、1、2、3、5、8、13、21、34、55、89……(也可以从0开始),前后两个数字的比值随着数列的增加,越来越接近黄金比值0.618。比如这里的89,把它想象成整个有序表的元素个数,而89是由前面的两个斐波那契数34和55相加之后的和,也就是说把元素个数为89的有序表分成由前55个数据元素组成的前半段和由后34个数据元素组成的后半段,那么前半段元素个数和整个有序表长度的比值就接近黄金比值0.618,假如要查找的元素在前半段,那么继续按照斐波那契数列来看,55 = 34 + 21,所以继续把前半段分成前34个数据元素的前半段和后21个元素的后半段,继续查找,如此反复,直到查找成功或失败,这样就把斐波那契数列应用到查找算法中了。
从图中可以看出,当有序表的元素个数不是斐波那契数列中的某个数字时,需要把有序表的元素个数长度补齐,让它成为斐波那契数列中的一个数值,当然把原有序表截断肯定是不可能的,不然还怎么查找。然后图中标识每次取斐波那契数列中的某个值时(F[k]),都会进行-1操作,这是因为有序表数组位序从0开始的,纯粹是为了迎合位序从0开始。
斐波那契算法步骤
斐波拉契法是一维搜索中压缩比最高的搜索算法。斐波拉契法基于斐波拉契数列产生比例值,斐波拉契数列{Fn}的定义如下:
参考:【python】最优化方法之一维搜索(黄金分割法+斐波那契法)
示例
上面没有看懂,可以结合示例来看
斐波那契数列的代码
F = [1, 1]
while F[-1] < 10000: #获得小于10000的斐波那契数列
F.append(F[-2] + F[-1])
print(F)
结果:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946]
斐波那契法一维搜索求极值例题
题目: 求函数A = 4*sinx *(1+cosx) 的最大值(x是角度) x的范围是0~90° 收敛精度ε = 0.05代码:
#求函数最大值
from sympy import *
a = 0 #区间下限
b = 90 #区间上限
dx = 0.05 #迭代精度
def f(x):
A = 4*sin(x*pi.evalf()/180)*(1+cos(x*pi.evalf()/180))
return A
N = (b-a)/dx
#获得斐波那契数列
F = [1, 1]
while F[-1] < N: #获得小于10000的斐波那契数列
F.append(F[-2] + F[-1])
print(F)
n = len(F) - 1 # 获取斐波那契数列的长度(计数从0开始)
if n < 3:
print("精度过低,无法进行斐波那契一维搜索")
else:
pass
x1 = a + F[n - 2] / F[n] * (b - a)
x2 = a + F[n - 1] / F[n] * (b - a)
t = n
i = 0
while (t > 3):
i += 1
if f(x1) < f(x2): # 如果f(x1)<f(x2),则在区间(x1,b)内搜索
a = x1
x1 = x2
x2 = a + F[t - 1] / F[t] * (b - a)
elif f(x1) > f(x2): # 如果f(x1)>f(x2),则在区间(a,x2)内搜索
b = x2
x2 = x1
x1 = a + F[t - 2] / F[t] * (b - a)
else: # 如果f(x1)=f(x2),则在区间(x1,x2)内搜索
a = x1
b = x2
x1 = a + F[t - 2] / F[t] * (b - a)
x2 = a + F[t - 1] / F[t] * (b - a)
t -= 1
#当t<3时
x1 = a + 0.5 * (b - a) # 斐波那契数列第3项和第2项的比
x2 = x1 + 0.1 * (b - a) # 偏离一定距离,人工构造的点
if f(x1) < f(x2): # 如果f(x1)<f(x2),则在区间(x1,b)内搜索
a = x1
elif f(x1) > f(x2): # 如果f(x1)>f(x2),则在区间(a,x2)内搜索
b = x2
else: # 如果f(x1)=f(x2),则在区间(x1,x2)内搜索
a = x1
b = x2
#打印结果
print(f'迭代第{i}次,迭代精度小于等于{dx},最终的搜索区间为:{min(a, b), max(a, b)}')
print(f'A的最大值:{f((a + b) / 2)}')
print('确定最大值的两端值为:', f(a), f(b))
结果:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584]
迭代第14次,迭代精度小于等于0.05,最终的搜索区间为:(59.97130178171193, 60.035878108471216)
A的最大值:5.19615240230772
确定最大值的两端值为: 5.19615111897465 5.19615038546139
对比黄金分割法:用黄金分割法(Golden Section Search Method)求函数最大值的python程序
斐波那契法迭代+绘图
from sympy import *
import matplotlib.pyplot as plt # 绘图模块
from pylab import * # 绘图辅助模块
#处理中文乱码
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
def f(x):
A = 4*sin(x*pi/180)*(1+cos(x*pi/180))
return A
# 绘图f(x)函数图像:给定闭区间(绘图间隔),绘图间隔默认为0.05,若区间较小,请自行修改
def drawf(a,b,dx):
x = [a + ele * dx for ele in range(0, int((b - a) / dx))]
y = [f(ele) for ele in x]
plt.figure(1)
plt.plot(x, y,
color = 'steelblue', # 折线颜色
marker = 'o', # 折线图中添加圆点
markersize = 0.01, # 点的大小
)
xlim(a, b)
# 添加图形标题
plt.title('f(x)函数图像')
plt.show()
#斐波那契一维搜索
def Fabonaci_search(a,b,dx):
N = (b-a)/dx
#获得斐波那契数列
F = [1, 1]
while F[-1] < N: #获得小于10000的斐波那契数列
F.append(F[-2] + F[-1])
print(F)
n = len(F) - 1 # 获取斐波那契数列的长度(计数从0开始)
if n < 3:
print("精度过低,无法进行斐波那契一维搜索")
else:
pass
x_values = []
x_values.append(a)
x_values.append(b)
x1 = a + F[n - 2] / F[n] * (b - a)
x2 = a + F[n - 1] / F[n] * (b - a)
t = n
i = 0
while (t > 3):
i += 1
if f(x1) < f(x2): # 如果f(x1)<f(x2),则在区间(x1,b)内搜索
a = x1
x1 = x2
x2 = a + F[t - 1] / F[t] * (b - a)
elif f(x1) > f(x2): # 如果f(x1)>f(x2),则在区间(a,x2)内搜索
b = x2
x2 = x1
x1 = a + F[t - 2] / F[t] * (b - a)
else: # 如果f(x1)=f(x2),则在区间(x1,x2)内搜索
a = x1
b = x2
x1 = a + F[t - 2] / F[t] * (b - a)
x2 = a + F[t - 1] / F[t] * (b - a)
t -= 1
x_values.append(a)
x_values.append(x1)
x_values.append(x2)
x_values.append(b)
#当t<3时
x1 = a + 0.5 * (b - a) # 斐波那契数列第3项和第2项的比
x2 = x1 + 0.1 * (b - a) # 偏离一定距离,人工构造的点
if f(x1) < f(x2): # 如果f(x1)<f(x2),则在区间(x1,b)内搜索
a = x1
elif f(x1) > f(x2): # 如果f(x1)>f(x2),则在区间(a,x2)内搜索
b = x2
else: # 如果f(x1)=f(x2),则在区间(x1,x2)内搜索
a = x1
b = x2
x_values.append(a)
x_values.append(x1)
x_values.append(x2)
x_values.append(b)
#打印结果
print(f'迭代第{i}次,迭代精度小于等于{dx},最终的搜索区间为:{min(a, b), max(a, b)}')
print(f'A的最小值:{f((a + b) / 2)}')
print('确定最大值的两端值为:', f(a), f(b))
draw(x_values)
#绘迭代图
def draw(x_values):
#设置绘图风格
plt.style.use('ggplot')
#处理中文乱码
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
#坐标轴负号的处理
plt.rcParams['axes.unicode_minus']=False
#横坐标是区间
#纵坐标是函数值
y_values = []
x_values.sort() #默认列表中的元素从小到大排列
for x in x_values:
y_values.append(f(x))
#绘制折线图
plt.plot(x_values,
y_values,
color = 'steelblue', # 折线颜色
marker = 'o', # 折线图中添加圆点
markersize = 3, # 点的大小
)
# 修改x轴和y轴标签
plt.xlabel('区间')
plt.ylabel('函数值')
# 添加图形标题
plt.title('Faboncci Method求函数最大值')
# 显示图形
plt.show()
if __name__ == '__main__':
a = 0 # 区间下限
b = 90 # 区间上限
dx = 0.05 # 迭代精度
drawf(a,b,dx) #绘制f(x)函数图像
Fabonaci_search(a, b, dx)
结果:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584]
迭代第14次,迭代精度小于等于0.05,最终的搜索区间为:(59.97130178171193, 60.035878108471216)
A的最小值:5.196152402307721
确定最大值的两端值为: 5.196151118974646 5.196150385461393