经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
雅可比行列式迭代及优化(golang版)
来源:cnblogs  作者:wang_yb  时间:2021/12/31 9:00:18  对本文有异议

最近遇到的一个求解雅可比迭代的问题,这个计算方法在 python 中有现成的库,但是在 golang 中没找到相应的实现。
于是根据雅可比行列式的推导实现了一个 golang 版本的雅可比迭代。
?

雅可比迭代

推导

一个 \(N \times N\) 的线性方程组 。

\[Ax = b \]

其中:

\[A = \begin{bmatrix} a_{11} & a_{12} & \cdots & a_{1n} \a_{21} & a_{22} & \cdots & a_{2n} \\vdots & \vdots & \ddots & \vdots \a_{n1} & a_{n2} & \cdots & a_{nn} \end{bmatrix}, x = \begin{bmatrix} x_{1} \x_{2} \\vdots \x_{n} \end{bmatrix}, b = \begin{bmatrix} b_{1} \b_{2} \\vdots \b_{n} \end{bmatrix} \]

\(A\) 分解成对角矩阵 \(D\) 和 其余部分 \(R\)
?

\[A = D + R,其中 D = \begin{bmatrix} a_{11} & 0 & \cdots & 0 \0 & a_{22} & \cdots & 0 \\vdots & \vdots & \ddots & \vdots \0 & 0 & \cdots & a_{nn} \end{bmatrix}, R = \begin{bmatrix} 0 & a_{12} & \cdots & a_{1n} \a_{21} & 0 & \cdots & a_{2n} \\vdots & \vdots & \ddots & \vdots \a_{n1} & a_{n2} & \cdots & 0 \end{bmatrix} \]

线性方程组可以改写为:

\[Dx = b - Rx \]

雅可比迭代就是在每一次的迭代中,上一次算出的 x 被用在右侧,用来计算左侧新的x
这个过程用公式表示如下:

\[x^{(k+1)} = D^{-1}(b - Rx^{(k)}) \]

每个元素就是:

\[x^{(k+1)}_{i} = \frac{1}{a_{ii}}(b_{i} - \sum_{j \neq i}a_{ij}x^{(k)}_{j}), i = 1,2,\cdots,n. \]

也就是:

\[x^{(k+1)}_{i} = \frac{b_{i}}{a_{ii}} - \sum_{j \neq i}\frac{a_{ij}}{a_{ii}}x^{(k)}_{j}, i = 1,2,\cdots,n. \]

实现函数

主要根据最后一步推导出的公式,实现如下:

  1. package main
  2. import (
  3. "fmt"
  4. "math"
  5. )
  6. // Jacobi 迭代法 输入系数矩阵 mx、值矩阵 mr、迭代次数 n、误差 c(以 list 模拟矩阵 行优先)
  7. func Jacobi(mx [][]float64, mr []float64, n int, c float64) ([]float64, error) {
  8. if len(mx) != len(mr) {
  9. return nil, fmt.Errorf("系数矩阵和值矩阵长度不匹配")
  10. }
  11. x := initX(len(mr)) // 迭代初始值
  12. //迭代次数控制
  13. for count := 0; count < n; count++ {
  14. nx := initX(len(x))
  15. for i := 0; i < len(x); i++ {
  16. nxi := mr[i]
  17. for j := 0; j < len(mx[i]); j++ {
  18. if j != i {
  19. nxi = nxi + (-mx[i][j])*x[j]
  20. }
  21. }
  22. nxi = nxi / mx[i][i] // 迭代计算得到的下一个 xi 值
  23. nx[i] = nxi
  24. }
  25. lc := make([]float64, 0) // 存储两次迭代结果之间的误差的集合
  26. for i := 0; i < len(x); i++ {
  27. lc = append(lc, math.Abs(x[i]-nx[i]))
  28. }
  29. if max(lc) < c {
  30. fmt.Printf("一共迭代 %d 次\n", count+1)
  31. return nx, nil
  32. }
  33. x = nx
  34. }
  35. return nil, fmt.Errorf("超过最大迭代次数,未找到解")
  36. }
  37. func initX(n int) []float64 {
  38. x := make([]float64, n) // 迭代初始值
  39. return x
  40. }
  41. func max(x []float64) float64 {
  42. var m float64
  43. for _, a := range x {
  44. if a > m {
  45. m = a
  46. }
  47. }
  48. return m
  49. }

测试代码:

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "testing"
  6. )
  7. func TestJacobi(t *testing.T) {
  8. mx := [][]float64{{8.0, -3.0, 2.0}, {4.0, 11.0, -1.0}, {6.0, 3.0, 12.0}}
  9. mr := []float64{20.0, 33.0, 36.0}
  10. ret, err := Jacobi(mx, mr, 100, 1e-20)
  11. if err != nil {
  12. log.Fatalln(err)
  13. }
  14. fmt.Printf("result: %v\n", ret)
  15. }

测试结果如下:

  1. $ go test -v
  2. === RUN TestJacobi
  3. 一共迭代 39
  4. result: [3 2 1]

雅可比迭代的改进

推导

上述的公式有一个可以改进的地方,每次迭代时,**i **之前的元素可以用本次迭代已经算出的值来替代。
也就是最终的公式变成:

\[x^{(k+1)}_{i} = \frac{b_{i}}{a_{ii}} - \sum_{j=1}^{i-1}\frac{a_{ij}}{a_{ii}}x^{(k+1)}_{j} - \sum_{j=i+1}^{n}\frac{a_{ij}}{a_{ii}}x^{(k)}_{j}, i = 1,2,\cdots,n. \]

实现方式

  1. func Jacobi2(mx [][]float64, mr []float64, n int, c float64) ([]float64, error) {
  2. if len(mx) != len(mr) {
  3. return nil, fmt.Errorf("系数矩阵和值矩阵长度不匹配")
  4. }
  5. x := initX(len(mr)) // 迭代初始值
  6. //迭代次数控制
  7. for count := 0; count < n; count++ {
  8. nx := initX(len(x))
  9. for i := 0; i < len(x); i++ {
  10. nxi := mr[i]
  11. for j := 0; j <= i-1; j++ {
  12. nxi = nxi + (-mx[i][j])*nx[j]
  13. }
  14. for j := i + 1; j < len(mx[i]); j++ {
  15. nxi = nxi + (-mx[i][j])*x[j]
  16. }
  17. nxi = nxi / mx[i][i] // 迭代计算得到的下一个 xi 值
  18. nx[i] = nxi
  19. }
  20. lc := make([]float64, 0) // 存储两次迭代结果之间的误差的集合
  21. for i := 0; i < len(x); i++ {
  22. lc = append(lc, math.Abs(x[i]-nx[i]))
  23. }
  24. if max(lc) < c {
  25. fmt.Printf("一共迭代 %d 次\n", count+1)
  26. return nx, nil
  27. }
  28. x = nx
  29. }
  30. return nil, fmt.Errorf("超过最大迭代次数,未找到解")
  31. }

测试函数:

  1. func TestJacobi2(t *testing.T) {
  2. mx := [][]float64{{8.0, -3.0, 2.0}, {4.0, 11.0, -1.0}, {6.0, 3.0, 12.0}}
  3. mr := []float64{20.0, 33.0, 36.0}
  4. ret, err := Jacobi2(mx, mr, 100, 1e-20)
  5. if err != nil {
  6. log.Fatalln(err)
  7. }
  8. fmt.Printf("result: %v\n", ret)
  9. }

?

测试结果如下:

  1. $ go test -v
  2. === RUN TestJacobi
  3. 一共迭代 39
  4. result: [3 2 1]
  5. --- PASS: TestJacobi (0.00s)
  6. === RUN TestJacobi2
  7. 一共迭代 19
  8. result: [3 2 1]
  9. --- PASS: TestJacobi2 (0.00s)
  10. PASS
  11. ok sample 0.009s

?

计算结果一样,但是迭代次数减少了很多。

原文链接:http://www.cnblogs.com/wang_yb/p/15744751.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号