介绍

数模总结写着写着发现最优化上机代码似乎还没留存😀发现有篇博客没水很不是滋味

遂附上啦,都封装好了自认为函数还行吧只需要自行修改输入x0就可以自用了:

最速下降法

牛顿法

拟牛顿法

修正牛顿法

单纯性法

坐标轮换法

CODE

import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
mpl.rcParams['font.sans-serif'] = 'SimHei'
mpl.rcParams['axes.unicode_minus'] = False



def num_grad(x, h): # 求梯度

# df=np.array()
df = np.zeros(x.size)
for i in range(x.size):
x1, x2 = x.copy(), x.copy() # 这里需要用到复制,而不能用赋值号(=),原因是Python里面=号只是取别名,不是复制(c/c++里面是)
x1[i] = x[i] - h
x2[i] = x[i] + h
y1, y2 = f(x1), f(x2)
df[i] = (y2 - y1) / (2 * h)
return df


def num_hess(x, h): # 求hess矩阵
hess = np.zeros((x.size, x.size))
for i in range(x.size):
x1 = x.copy()
x1[i] = x[i] - h
df1 = num_grad(x1, h)
x2 = x.copy()
x2[i] = x[i] + h
df2 = num_grad(x2, h)
d2f = (df2 - df1) / (2 * h)
hess[i] = d2f
return hess


def linesearch(x, pk:float,f):
'''
黄金分割法
'''
eps = 0.001
r = 500
l = -500

while r - l > eps:
t1 = l + 0.382 * (r - l)
t2 = l + 0.618 * (r - l)

x_1 = x+ t1 * pk
x_2 = x + t2 * pk
f1 = f(x_1)
f2 = f(x_2)
if f1 < f2:
r = t2

else:
l = t1
if t2 - t1 < eps:
return t2


def steepest(x,f,epsilon=0.01,h=10**-5,maxiter=10**4):
'''
最速下降法
:param x: 初始值
:param f: 函数
:param epsilon: eps
:param h: 求导的delta x
:param maxiter: 最大迭代次数
:return: 答案的list用于画图
'''
x_list=[]
ans_list=[]
x_list.append(x)
ans_list.append(f(x))
for iter1 in range(maxiter):
grad = num_grad(x, h)

pk = -grad
ak = linesearch(x, pk,f)
x = x + ak * pk
print('grad:', grad, 'x_{i+1}:', x, 't:', ak,'ans:',f(x))
x_list.append(x)
ans_list.append(f(x))
if np.linalg.norm(grad) < epsilon:
return x_list,ans_list
return x_list,ans_list


def newTonFuction(x,f,epsilon=0.01,h1=10**-5,h2=10**-5,maxiter=10**4): # 牛顿法

x_list=[]
ans_list=[]
x_list.append(x)
ans_list.append(f(x))
for iter1 in range(maxiter):
grad = num_grad(x, h1)

hess = num_hess(x, h2)
pk = -np.dot((np.linalg.inv(hess)), grad)
x = x + pk
x_list.append(x)
ans_list.append(f(x))
print('grad:', grad, 'x_{i+1}:', x, 'ans:', f(x))
if np.linalg.norm(grad) < epsilon:
return x_list,ans_list
return x_list,ans_list


def BFGS(x,f,epsilon=0.01,h=10**-5,maxiter=10**4):
'''
拟牛顿法
:param x: 初始点
:param f: 求解函数
:param epsilon: eps
:param h: 求导的delta x
:param maxiter: 最大迭代数
:return: 答案的list便于画图
'''
Bk = np.eye(x.size)
x_list=[]
ans_list=[]
x_list.append(x)
ans_list.append(f(x))
for i in range(maxiter):
grad = num_grad(x, h)

pk = -np.dot((np.linalg.inv(Bk)), grad)
ak = linesearch(x, pk,f)
x = x + pk * ak
yk = num_grad(x, h) - grad
sk = ak * pk
if np.dot(yk.reshape(1, grad.shape[0]), sk) > 0:
Bk = Bk - np.dot(np.dot(np.dot(Bk, sk).reshape(sk.shape[0], 1), sk.reshape(1, sk.shape[0])), Bk)\
/ np.dot(np.dot(sk.reshape(1, sk.shape[0]), Bk), sk) + \
np.dot(yk.reshape(yk.shape[0], 1),yk.reshape(1, yk.shape[0])) / \
np.dot(yk.reshape(1, yk.shape[0]), sk)
print('grad{%d}:' % (i + 1), np.round(num_grad(x, h), 3),
'p{%d}:' % (i + 1), np.round(pk, 3),
'x_{%d}:' % (i + 1), np.round(x, 3), f(x))
x_list.append(x)
ans_list.append(f(x))
if np.linalg.norm(grad) < epsilon:
return x_list,ans_list
return x_list,ans_list

def modify_newton(x,f,epsilon=0.01,h1=10**-5,h2=10**-5,maxiter=10**4):
ans_list=[]
x_list=[]
x_list.append(x)
ans_list.append(f(x))
for i in range(maxiter):
grad=num_grad(x,h1)
hess = num_hess(x, h2)
pk = -np.dot((np.linalg.inv(hess)), grad)
t=linesearch(x,pk,f)
x = x + pk*t
print('grad:',np.round(grad,3),'pk:',np.round(pk,3),'t:',np.round(t,3),'x_{%d}:'%i,np.round(x,3),f(x))
x_list.append(x)
ans_list.append(f(x))
if np.linalg.norm(grad)<epsilon:
return x_list,ans_list

return x_list,ans_list

def conjugate_gradient(x,f,epsilon=0.01,h1=10**-5,h2=10**-5,maxiter=10**4):
ans_list = []
x_list = []
x_list.append(x)
ans_list.append(f(x))
pk=-num_grad(x,h1)
if np.linalg.norm(pk)<epsilon:
return x
for i in range(maxiter):

grad=num_grad(x,h1)
t=np.round(linesearch(x,pk,f),2)
used_x=x.copy()
x = x + pk*t

pk=-num_grad(x,h1)+(np.linalg.norm(num_grad(x,h1))**2/np.linalg.norm(num_grad(used_x,h1))**2)*pk
print('grad{%d}:'%(i+1),np.round(num_grad(x,h1),3),'p{%d}:'%(i+1),np.round(pk,3),'t_{%d}:'%(i+1),np.round(t,3),'x_{%d}:'%(i+1),np.round(x,3),f(x))
x_list.append(x)
ans_list.append(f(x))
if np.linalg.norm(grad)<epsilon:
return x_list,ans_list

return x_list,ans_list

def simplex_method(x:list,f,maxiter:int=100,alpha:float=1.,beta:float=1.,epsilon:float=0.01):

temp_x_list=x
x_list=[]
ans_list=[]

for i in range(maxiter):
l=[]
for j in temp_x_list:
l.append(f(j))
l=np.array(l)
x_min = temp_x_list[l.argsort()[0]]
x_mid = temp_x_list[l.argsort()[1]]
x_max = temp_x_list[l.argsort()[2]]
loss=sum([(f(j)-f(x_min))**2 for j in x])


tempx=(np.sum(x,axis=0)-x_max)/ (len(x)-1)
reflect_x=2*tempx-x_max
print('reflect_x:',reflect_x)
if f(x_min)>f(reflect_x):
inflation_x=tempx+alpha*(reflect_x-tempx)
if f(inflation_x)<f(reflect_x):
temp_x_list[l.argsort()[2]]=inflation_x
else:
temp_x_list[l.argsort()[2]]=reflect_x
print('f(x_min):%.3f>f(reflect):%.3f'%(f(x_min),f(reflect_x)))
print('inflation_x:',inflation_x)

if f(x_min)<=f(reflect_x) and f(reflect_x)<f(x_mid):
temp_x_list[l.argsort()[2]]=reflect_x
print('f(x_min):%.3f<=f(reflect):%.3f<=f(x_mid):%.3f' % (f(x_min), f(reflect_x),f(x_mid)))


if f(x_mid)<=f(reflect_x) and f(reflect_x)<f(x_max):
shrink_x=tempx+beta*(reflect_x-tempx)
temp_x_list[l.argsort()[2]]=shrink_x
print('f(x_mid):%.3f<=f (reflect):%.3f <f(x_max):%.3f' % (f(x_mid),f(reflect_x), f(x_max)))
print('shrink_x:',shrink_x)
if f(reflect_x)>=f(x_max):
# 是xmax !
shrink_x=tempx+beta*(x_max-tempx)
if f(shrink_x)>f(x_max):
# shrink too less
for j in x:
j=(x_min+j)/2
temp_x_list=x
else:
temp_x_list[l.argsort()[2]]=shrink_x
print('f(reflect):%.3f>f(x_max):%.3f' % (f(reflect_x),f(reflect_x)))
print('shrink_x:',shrink_x)
# print("xmin:",x_min,'xmid:',x_mid,'xmax:',x_max)
# print('min:',f(x_min),'fmid',f(x_mid),'fmax',f(x_max))
print('temp_x_list:',temp_x_list)
print('loss:%.3f' % loss)
if loss < epsilon:

break
x_list.append(x_min)
ans_list.append(f(x_min))
print()
return x_list,ans_list


def cyclic_coordinate_method(x:list,f,epsilon:float=0.01,h1:float=1e-5,maxiter:int=100):
ans_list = []
x_list = []
x_list.append(x)
ans_list.append(f(x))

I=np.eye(2)
for i in range(maxiter):
used_x=x.copy()
for pk in I:
t = np.round(linesearch(x, pk,f),2)
x = x + pk * t
print('p{%d}:' % (i + 1), np.round(pk, 3),
't_{%d}:' % (i + 1), np.round(t, 2), 'x_{%d}:' % (i + 1), np.round(x, 3), f(x),'eps:',np.linalg.norm(used_x-x))
x_list.append(x)
ans_list.append(f(x))
if np.linalg.norm(used_x-x)< epsilon:
return x_list,ans_list
return x_list,ans_list


def draw(x:list=None,y:list=None,ans:list=None):

fig=plt.figure()
ax = fig.gca(projection='3d')

x1 = np.linspace(min(x),max(x), 100)
x2 = np.linspace(min(x), max(x), 100)
# print(f(x1,x2))

tempx, tempy = np.meshgrid(x1, x2)
surf = ax.plot_surface(tempx, tempy, f([tempx,tempy]), cmap=cm.jet, zorder=10)

scat=ax.scatter(x,y,ans,zorder=1000,marker='x')
line=ax.plot(x,y,ans,zorder=100,linestyle='--')
contour=ax.contour(tempx,tempy,f([tempx,tempy]),50,zorder=100000)
plt.legend()
plt.show()
return

def f(x): # 目标函数
x1 = x[0]
x2 = x[1]
# test
y=x1**2+x2**2+(x2-1)**2

# y=(x1-2)**4+(x1-2*x2)**2

# 修正牛顿法函数
# y = 4*(x1+1)**2 +2*(x2-1)**2+x1+x2+10
# 最速下降法函数
# y=x1**2+25*x2**2
# 牛顿法函数
# y=60-10*x1-4*x2+x1**2+x2**2-x1*x2
# 共轭梯度法函数
# y=x1**2+4*x2**2
# DFP函数
# y=4*(x1-5)**2+(x2-6)**2
# 坐标轮换法
# y=x1**2+2*x2**2-x1*x2-10*x1-4*x2+60
# 单纯形法
# y=x1**2+2*x2**2-4*x1-8*x2+5
return y

if __name__ == '__main__':
# 后面不附重复代码和函数
x0 = np.array([1., 1.]) # 初始解
# x1= np.array([0.965,0.259])
# x2=np.array([0.259,0.965])
# x_list,ans_list=simplex_method([x0,x1,x2],f,epsilon=0.1,alpha=1.1,beta=0.5)
x_list,ans_list=BFGS(x0,f,maxiter=100)
draw([i[0] for i in x_list],[i[1] for i in x_list],ans_list)
# 修正牛顿法
# x_list,ans_list=modify_newton(x0)
# 共轭梯度
# x_list,ans_list=conjugate_gradient(x0)

# 最速下降法
# x_list,ans_list = steepest(x0)

# 调用牛顿法
# x_list,ans_list = newTonFuction(x0)

# 调用拟牛顿法
# x_list,ans_list = BFGS(x0)

有bug可以自行修改一下linersearch范围试试,这种bug懂的都懂。