流星雨 发表于 2020-11-30 16:41:32

【Julia1.5.2】求解对称正定线性方程组的共轭梯度法

本帖最后由 流星雨 于 2020-11-30 17:13 编辑

续watermelon主题:https://www.0xaa55.com/thread-26183-1-1.html中的雅各比迭代法,整理另一种“半迭代半直接”的求解对称正定线性方程组的共轭梯度法。

参考数目:
【1】《数值分析》(李乃成、梅立泉编著,科学出版社,ISBN:978-7-03-032192-3)
采用Julia语言(1.5.2)编制脚本Conjugate Gradient,主要功能均为求解方程组Ax=b。
脚本中的函数Conjugate_Gradient为共轭梯度求解函数,其输入量为矩阵A、尾向量b和迭代偏差ε,输出量为解向量x。
脚本最后提供了以Julia的LinearAlgebra包中附带的QR分解法求得的验证性解向量x2,给出了x2的值、x2与x的误差向量delta、delta的二范数‖delta‖2^2。

结构:


算法原理:


使用说明:
1.安装Julia编译环境JuliaPro-1.5.2-1。可在URL:https://juliacomputing.com/products/juliapro/,下载最新版的JuliaPro-1.5.3-1。
2.安装完毕后用Juliapro打开文件Conjugate gradient.jl,点击▶即可运行。UI右侧Workspace窗口可实时显示各变量值。
3.在脚本文件Conjugate gradient.jl中可以直接更改矩阵A、尾向量b ⃗和迭代偏差ε的值,再次点击▶即可重新计算。

附上参考书【1】给出的使用建议:
1、共轭梯度法只涉及矩阵与向量的乘法和向量内积运算,在理论上对于n阶线性方程组,最多n次迭代即可求得准确解,更像是一种直接解法;
2、对与良态方程,通常所需的迭代次数远小于矩阵阶数n;
3、对于病态问题,迭代次数有所增加,且计算精度可能受限。特别地,当迭代次数=矩阵阶数n时若仍未收敛,则应继续迭代。

源码:
#conjugate gradient Method
using LinearAlgebra
A = [
3 0 1 0 0;
0 5 0 0 0;
1 0 7 0 1;
0 0 0 5 0;
0 0 1 0 3;
]
b = '
ε = 1e-6 #误差
function Conjugate_Gradient(A,b,ε)
    N = length(b) #迭代步数
    x = ones(length(b),N)*ε #声明临时解向量
    r = zeros(length(b),N) #声明临时残向量
    d = zeros(length(b),N) #声明临时d向量
    α = zeros(N) #声明临时α常数
    β = zeros(N) #声明临时β常数
    r[:,1] = b - A*x[:,1] #求解r
    if r[:,1] == 0#如果初值猜对了,给出x
      x[:,N] = x[:,1]
    else #如果初值不对,共轭梯度法计算x
      d[:,1] = r[:,1] #求解d
      global ∑r2 = 1
      global k = 1
      while ∑r2 >= ε
            if k == N #循环次数=矩阵阶数,退出
                break
            end
             #循环次数<矩阵阶数,继续计算
            α = r[:,k]'*r[:,k]/(d[:,k]'*A*d[:,k])
            x[:,k+1] = x[:,k] + α*d[:,k]
            r[:,k+1] = b - A*x[:,k+1]
            #求解r的二范数平方
            global ∑r2 = 0
            for m = 1:length(b)
                global ∑r2 = ∑r2 + r^2
            end
            if (∑r2 <= ε)
                break
            end
            β = r[:,k+1]'*r[:,k+1]/(r[:,k]'*r[:,k])
            d[:,k+1] = r[:,k+1] + β*d[:,k]
            k = k+1
      end
    end
    return x
end
x = Conjugate_Gradient(A,b,ε)[:,k] #共轭梯度计算结果
x2 = (inv(qr(A).R)*qr(A).Q'*(b)) #QR分解计算结果
delta = zeros(length(b)) #误差向量
Σdelta = 0
for i = 1:length(b) #计算误差向量
    delta = (x - x2)/x2
    global Σdelta = Σdelta + delta^2
end
Σdelta = Σdelta^0.5 #计算误差二范数

流星雨 发表于 2020-12-26 19:34:51

watermelon 发表于 2020-12-26 17:10
Python写的共轭梯度法来啦!
# -*- coding: utf-8 -*-
"""


numpy确实方便!

watermelon 发表于 2020-12-26 17:10:45

Python写的共轭梯度法来啦!
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 26 14:54:24 2020
@copyright: Xi'an Jiaotong University - NuTHeL
@author: Zhou Xingguang
"""

import numpy as np
import copy

class ConjugateGradient(object):
    def __init__(self, matrix, b, x):
      # define the little number
      self.__ERR = 1E-6# private parameter
      # define the MAX_STEP
      self.__MAX_STEP = 1000 # private parameter
      self.matrix = np.array(copy.deepcopy(matrix))
      self.b = np.array(copy.deepcopy(b)).reshape(np.size(b), 1)
      self.x = np.array(copy.deepcopy(x)).reshape(np.size(b), 1)
      self.last_x = self.x[:, :]
      
      self.r = np.array(np.zeros((np.size(b), 1), dtype=float))
      self.last_r = self.r[:, :]
      self.d = np.array(np.zeros((np.size(b), 1), dtype=float))
      self.last_d = self.d[:, :]
      
      self.alpha = 0
      self.beta = 0
      self.time = 0
      
   
    def CG(self):
      self.r = self.b - np.matmul(self.matrix, self.x)
      self.last_r = self.r[:, :]
      self.d = self.r[:, :]
      for time in range(self.__MAX_STEP):
            self.alpha = np.linalg.norm(self.r, ord=2)**2 / np.matmul(np.matmul(np.transpose(self.d), self.matrix), self.d)
            self.last_x = self.x[:, :]
            self.x = self.x + self.alpha * self.d
            self.last_r = self.r[:, :]
            self.r = self.b - np.matmul(self.matrix, self.x)
            self.beta = (np.linalg.norm(self.r, ord=2)**2) / (np.linalg.norm(self.last_r, ord=2)**2)
            self.d = self.r + self.beta*self.d
            self.alpha = (np.linalg.norm(self.r)**2) / np.matmul(np.matmul(np.transpose(self.d), self.matrix), self.d)
            if np.linalg.norm(self.r) <= self.__ERR:
                print("iteration has been converged...\n")
                self.time = time + 1
                return self.x
      print("The result is not converged, please recheck the method")
      return None

def GenerateExercise(matrix, b):
    cnt = np.size(b)
    b = -1
    b = -1
   
    for i in range(cnt):
      if i == 0:
            matrix = -2
            matrix = 1
      elif i == cnt-1:
            matrix = -2
            matrix = 1
      else:
            matrix = -2
            matrix = 1
            matrix = 1
    return matrix, b


if __name__ == '__main__':
    cnt = 400
    matrix = np.zeros((cnt, cnt), dtype=float)
    x = np.zeros((cnt, 1), dtype=float)
    b = np.zeros((cnt, 1), dtype=float)
   
    matrix, b = GenerateExercise(matrix, b)
   
    CG = ConjugateGradient(matrix, b, x)
    result = CG.CG()
    print(result)
    print(CG.time)

watermelon 发表于 2020-12-10 22:25:51

再来一个小弟写的HouseHolder变换的QR分解:

0xAA55 发表于 2020-12-11 01:55:58

这些数学公式我看了头大。。。
页: [1]
查看完整版本: 【Julia1.5.2】求解对称正定线性方程组的共轭梯度法