共軛梯度法是利用目標函數的梯度逐步產生共軛方向並將其作爲搜索方向的方法。
1. 共軛方向與共軛方向法
定義:設H是n*n方陣且對稱正定。
(1)若對n維非零向量p和q,有p^THq = 0,則稱p和q是H-共軛的;
(2)若對n維非零向量組d1,...,dm,對任意的i != j,di與dj是H-共軛的,則稱d1,...,dm是H-共軛方程組。
當H = I,p^Tq = 0,即p與q相互正交,可見共軛是正交的推廣。
定理:設H是n*n方陣且對稱正定。若n維非零向量組d1,...,dm是H-共軛方程組,則d1,...,dm也是線性無關方程組。
將一組共軛方向作爲搜索方向對無約束非線性規劃問題(UNP)進行求解的方法稱爲共軛方向法。
2. 無約束凸二次規劃問題的共軛梯度法
min f(x) = 1/2 * x^T * H * x + c^T * x + b
其中矩陣H是n*n方陣,且對稱正定,f(x)是嚴格凸函數。顯然,▽f(x) = Hx + c,▽^2f(x) = H,並且x是UQP的最優解條件是▽f(x) = 0。
若用一組共軛方向作爲搜索方向求解UQP,那麼最多迭代n次即可得到其最優解,因此共軛方向法具有二次終止性。
算法如下:
①選定初始數據。給定初始點x,令k = 1。
②最優性判別。若▽f(x) = 0,停止,得到最優解x。否則轉3。
③構造搜索方向。當k = 1時令d(1) = - ▽f(x),當k>1時令d(k) = -g(k) + β*d(k-1),其中β = ||g(k)||^2 / ||g(k-1)||^2確定。
g(k) = ▽f(x_k)。
④確定新的迭代點。令λ = - g(k)^T * d(k) / d(k)^T * H * d(k),x = x + λ*d(k)。若k = n,停止,得最優解爲x;否則k = k +1,轉2。
求解代碼如下:
'''
1/2 x^ * H * x + c^ * x + b
'''
import numpy as np
def algo(H, c, b, x0):
x = x0
k = 1
d, d_pre = None, None
g, g_pre = None, None
beta = None
while(True):
g = np.dot(H, x) + c
norm = np.linalg.norm(g)
if(norm == 0):
break
if(k == 1):
d = -g
else:
beta = (float) (np.linalg.norm(g)**2 / np.linalg.norm(g_pre)**2)
d = -g + beta * d_pre
lambda_ = -np.sum(float(g.transpose().dot(d)) / (d.transpose().dot(H).dot(d)))
x = x + lambda_ * d
print 'k = %s;g = %s;norm = %s;beta = %s;lambda = %s' %(str(k), str(g), str(norm), str(beta), str(lambda_))
if(k == H.shape[0]):
break
else:
k += 1
d_pre = d
g_pre = g
return x