最优化理论——牛顿法(一维、多维)原理及python代码实现
代码如下,如果只想抄个模板可以直接复制一维牛顿法代码二维牛顿法实现。
目录
代码如下,如果只想抄个模板可以直接复制
一维牛顿法代码
from sympy import *
import pylab
x = symbols('x')
fun = eval(input("请输入一维函数"))
x_0 = float(input("请输入初始点"))
epsilon = float(input("输入精度"))
diff_1 = diff(fun)
diff_2 = diff(diff_1)
def newton(x_0,epsilon):
while True:
try:
x_0 = x_0 - diff_1.subs(x,x_0) / diff_2.subs(x,x_0)
except ZeroDivisionError:
raise ZeroDivisionError("Could not find root") from None
if abs(diff_1.subs(x,x_0)) < epsilon:
return x_0
x_min = newton(x_0,epsilon)
print(x_min)
print("所求极值点为{:.2f}:".format(x_min))
print("所求极值为{:.5f}".format(fun.subs(x,x_min)))
def drawf(a,b,interp):
x_la = [a + ele * interp for ele in range(0, int((b - a) / interp))]
y_la = []
for ele in x_la:
y_la.append(fun.subs(x,ele).evalf())
pylab.plt.figure(1)
pylab.plt.plot(x_la, y_la)
pylab.xlim(a, b)
pylab.plt.show()
drawf(-10,10,0.05)
二维牛顿法实现
from sympy import *
import pylab
import numpy as np
import math
x1 = symbols('x1')
x2 = symbols('x2')
fun = eval(input("请输入二维函数(用x1,x2定义)"))
x_0 = input("请输入初始点,用逗号隔开")
x_0 = x_0.split(',')
data = []
for i in x_0:
data.append(float(i))
print(data)
epsilon = float(input("输入精度"))
def grad_len(grad):
vec_len = math.sqrt(pow(grad[0], 2) + pow(grad[1], 2))
return vec_len
def newton_dou(x_init, fun,epsilon):
# 初始点的梯度值
grandient_obj = np.array([diff(fun, x1).subs(x1, x_init[0]).subs(x2, x_init[1]),
diff(fun, x2).subs(x1, x_init[0]).subs(x2, x_init[1])], dtype=float)
# 初始点的hessian矩阵
hessian_obj = np.array([[diff(fun, x1, 2).subs(x1, x_init[0]).subs(x2, x_init[1]),
diff(diff(fun, x1), x2).subs(x1, x_init[0]).subs(x2, x_init[1])],
[diff(diff(fun, x2), x1).subs(x1, x_init[0]).subs(x2, x_init[1]),
diff(fun, x2, 2).subs(x1, x_init[0]).subs(x2, x_init[1])]],dtype=float)
# 海森矩阵求逆
inverse = np.linalg.inv(hessian_obj)
#inverse = np.linalg.inv(hessian_obj)
if(grad_len(grandient_obj)>epsilon):
# 第一次迭代公式
x_new = x_init - np.matmul(inverse, grandient_obj)
#print(x_new)
# print('迭代第%d次:%.5f' %(i, x_new))
# 当前点的梯度值
grandient_obj = np.array([diff(fun, x1).subs(x1, x_new[0]).subs(x2, x_new[1]),
diff(fun, x2).subs(x1, x_new[0]).subs(x2, x_new[1])], dtype=float)
# 当前点的hessian矩阵
hessian_obj = np.array([[diff(fun, x1, 2).subs(x1, x_new[0]).subs(x2, x_new[1]),
diff(diff(fun, x1), x2).subs(x1, x_new[0]).subs(x2, x_new[1])],
[diff(diff(fun, x2), x1).subs(x1, x_new[0]).subs(x2, x_new[1]),
diff(fun, x2, 2).subs(x1, x_new[0]).subs(x2, x_new[1])]],dtype=float)
inverse = np.linalg.inv(hessian_obj) # hessian矩阵求逆
x_new = x_new - np.matmul(inverse, grandient_obj) # 迭代公式
#print(x_new)
return x_new
x_min = newton_dou(data,fun,epsilon)
print("所求的最小值点:x1为{:.5f},x2为{:.5f}".format(x_min[0],x_min[1]))
print("所求的最小值为{:5f}".format(fun.subs({x1:x_min[0],x2:x_min[1]})))
1.一维牛顿法
一维牛顿法(One-Dimensional Newton's Method)是一种在最优化问题中使用的迭代算法,用于寻找函数的极值点。它是基于牛顿法的一维优化版本,适用于单变量函数。
1.1一维牛顿法原理
原理介绍:对于要求的函数q(x),我们使用泰勒展开式,假设q(x)在所求区间内二阶可导,可推导:
对q(x)求导得:
要求极值,我们令q'(x) = 0,得x的表达式
由此,我们得到了迭代公式,我们只需要每次更新x,并且设置一个精度,当:
停止迭代.
1.2一维牛顿法代码实现
一维牛顿法代码实现比较简单,代码如下。
我们在此输入函数为5*x**2 +2*x +3 精度为0.001,初始点为10
from sympy import *
import pylab
x = symbols('x')
fun = eval(input("请输入一维函数"))
x_0 = float(input("请输入初始点"))
epsilon = float(input("输入精度"))
diff_1 = diff(fun)
diff_2 = diff(diff_1)
def newton(x_0,epsilon):
while True:
try:
x_0 = x_0 - diff_1.subs(x,x_0) / diff_2.subs(x,x_0)
except ZeroDivisionError:
raise ZeroDivisionError("Could not find root") from None
if abs(diff_1.subs(x,x_0)) < epsilon:
return x_0
x_min = newton(x_0,epsilon)
print(x_min)
print("所求极值点为{:.2f}:".format(x_min))
print("所求极值为{:.5f}".format(fun.subs(x,x_min)))
运行结果为
绘制原函数图像,这里我们定义了a,b和interp的取值分别为-10,10,0.05。大家可以根据自己需求进行修改。
def drawf(a,b,interp):
x_la = [a + ele * interp for ele in range(0, int((b - a) / interp))]
y_la = []
for ele in x_la:
y_la.append(fun.subs(x,ele).evalf())
pylab.plt.figure(1)
pylab.plt.plot(x_la, y_la)
pylab.xlim(a, b)
pylab.plt.show()
drawf(-10,10,0.05)
绘制的图像为
2.多维牛顿法
2.1多维牛顿法原理
多维牛顿法,在形式上与一维牛顿法相似:
由泰勒公式求f(x)在x_k处的展开:
求导得:要求海森矩阵为正定,可逆的,并且为对称矩阵
得到迭代公式
算法设计流程
(1). 给定初始点 允许误差 >0
(2) 计算梯度,Hesse矩阵和牛顿方向
(3) 若 停止计算,输出
否则:牛顿迭代
牛顿法虽然简单,收敛速度快(能够达到二次收敛),但也有许多缺点
(1)局部收敛性,收敛点必须非常接近最优解
(2)计算复杂度高
(3)hesse矩阵可能为奇异矩阵,其逆矩阵不一定存在
2.2多维牛顿法代码实现
导入相关的包
from sympy import *
import pylab
import numpy as np
import math
输入和定义相关的参数,或者函数
输入的函数为 x1**2 + x1*x2*5 +x2**4
初始点为 10,10 (注意逗号必须是英文符号)
x1 = symbols('x1')
x2 = symbols('x2')
fun = eval(input("请输入二维函数(用x1,x2定义)"))
x_0 = input("请输入初始点,用逗号隔开")
x_0 = x_0.split(',')
data = []
for i in x_0:
data.append(float(i))
print(data)
epsilon = float(input("输入精度"))
精度设为0.0001
定义模函数,这里是二范数
def grad_len(grad):
vec_len = math.sqrt(pow(grad[0], 2) + pow(grad[1], 2))
return vec_len
定义牛顿方法
def newton_dou(x_init, fun,epsilon):
# 初始点的梯度值
grandient_obj = np.array([diff(fun, x1).subs(x1, x_init[0]).subs(x2, x_init[1]),
diff(fun, x2).subs(x1, x_init[0]).subs(x2, x_init[1])], dtype=float)
# 初始点的hessian矩阵
hessian_obj = np.array([[diff(fun, x1, 2).subs(x1, x_init[0]).subs(x2, x_init[1]),
diff(diff(fun, x1), x2).subs(x1, x_init[0]).subs(x2, x_init[1])],
[diff(diff(fun, x2), x1).subs(x1, x_init[0]).subs(x2, x_init[1]),
diff(fun, x2, 2).subs(x1, x_init[0]).subs(x2, x_init[1])]],dtype=float)
# 海森矩阵求逆
inverse = np.linalg.inv(hessian_obj)
#inverse = np.linalg.inv(hessian_obj)
if(grad_len(grandient_obj)>epsilon):
# 第一次迭代公式
x_new = x_init - np.matmul(inverse, grandient_obj)
#print(x_new)
# print('迭代第%d次:%.5f' %(i, x_new))
# 当前点的梯度值
grandient_obj = np.array([diff(fun, x1).subs(x1, x_new[0]).subs(x2, x_new[1]),
diff(fun, x2).subs(x1, x_new[0]).subs(x2, x_new[1])], dtype=float)
# 当前点的hessian矩阵
hessian_obj = np.array([[diff(fun, x1, 2).subs(x1, x_new[0]).subs(x2, x_new[1]),
diff(diff(fun, x1), x2).subs(x1, x_new[0]).subs(x2, x_new[1])],
[diff(diff(fun, x2), x1).subs(x1, x_new[0]).subs(x2, x_new[1]),
diff(fun, x2, 2).subs(x1, x_new[0]).subs(x2, x_new[1])]],dtype=float)
inverse = np.linalg.inv(hessian_obj) # hessian矩阵求逆
x_new = x_new - np.matmul(inverse, grandient_obj) # 迭代公式
#print(x_new)
return x_new
调用相关函数
x_min = newton_dou(data,fun,epsilon)
print("所求的最小值点:x1为{:.5f},x2为{:.5f}".format(x_min[0],x_min[1]))
print("所求的最小值为{:5f}".format(fun.subs({x1:x_min[0],x2:x_min[1]})))
得到运行结果
更多推荐
所有评论(0)