反復法を用いた連立方程式の解法
0.はじめに
本記事は学校の課題でレポートを作成する際に、「scrapboxで作れば楽なのでは?」という発想の下に作られたものです。
なので、構成や言葉遣いなどが記事っぽくない箇所があります。
ですが、反復法の説明としては...まぁそこそこ?...中の下くらいの価値はあるでしょうし、何よりも「折角、作ったのに消すのが勿体無い」と感じてしまったので残っています。
1.連立方程式のアルゴリズム的解法
連立方程式をアルゴリズム的に解く手法としては以下のようなものがある。
フラメルの公式
ガウス・ジョルダン法(はきだし法)
ガウスの消去法
LU分解
これらの手法は一般的には「代数的手法」と呼ばれる。
こららの代数的手法の特徴は厳密に値を求めるところにある。
反して、今回行う反復法は近似的に値を求める。
従って、この手法の評価点は精密さよりも処理速度にある。
2. 反復法
今回実装する手法は
ヤコビ法
ガウス・ザイデル法
の二つである。
この二つの手法は理論が似通っているため習得に連続性が期待できる。
次は共通の箇所だが、双方とも近似値を求めるプログラムなので「精度値」としてプログラム中に終了条件を記述する必要がある。
また、近似前の初期値となる値も定める必要があり、その初期値の要素数は連立方程式の変数の個数と同当分が必須となる。
3.反復法における収束性
今回のヤコビ法とガウス・ザイデル法は巨大な連立方程式を解くことを想定している。そして、大概にしてそのようなデータ群は端の値が小さくなるような優対角である。しかしながら、本実装でのプログラムではデータ群をランダムに設定しているため優対角でないことが多々ある。そのような状況となった場合に本反復法は一意の値に収束するのではなく発散するように莫大な数値となる。本来であれば、これ状況を防ぐもしくは緩和させる手法もあるが、今回は「エラー」としてプログラムを終了することとした。 4.ヤコビ法
ヤコビ法という呼称の数学手法は二つあるとされるが、本稿で扱うのは「連立一次方程式を近似的に解く手法」のヤコビ法であることに留意して頂きたい。
この手法名は解析学においても重要な功績を遺したとされるドイツ数学者のカール・グスタフ・ヤコブ・ヤコビの名に因んでいる。
例
$ \begin{pmatrix}a_1x+b_1y+c_1z \\ a_2x+b_2y+c_2z \\ a_3x+b_3y+c_3z\end{pmatrix}=$ \begin{pmatrix}D_1 \\ D_2 \\ D_3 \end{pmatrix}
例のような行列式のときに上の式に注目する。
$ a_1x + b_1y +c_1 = D_1
$ x = \tfrac{D_1 - b_1y - c_1z}{ a_1}
この操作を二番目の式はyに三番目の式はzにそれぞれ行う。
すると、各式に初期値を代入した際に、その更新値が得られるので、再度その更新値を初期値としてヤコビ法を行う。
これが反復法と言われる所以である。
5.ガウス・ザイデル法
この手法名は光学者として光度計測の研究を行ったとされるドイツ数学者であり光学者、天文学者のリートヴィヒ・ザイデルの名に因んでいる。
例
$ \begin{pmatrix}a_1x+b_1y+c_1z \\ a_2x+b_2y+c_2z \\ a_3x+b_3y+c_3z\end{pmatrix}=$ \begin{pmatrix}D_1 \\ D_2 \\ D_3 \end{pmatrix}
例のような行列式のときに上の式に注目する。
$ a_1x + b_1y +c_1 = D_1
$ x = \tfrac{D_1 - b_1y - c_1z}{ a_1}
この操作を二番目の式はyに三番目の式はzにそれぞれ行う。
ここまではヤコビ法と同様であるが、この後に各式に初期値を代入するのではなく、得られた更新値(例:x)を次の更新式(例:y)に代入する、再度その更新値を更新式に代入する。
ヤコビ法は全体に値を代入し、一括で処理を行っていたが、ガウス・ザイデル法は逐次的に値を代入し、最底辺式まできたらそれらの値を初期値として反復法としてガウス・ザイデル法を行う。
6.実験結果
本実験では以下の行列式を二つの手法で解き、比較するというものである。
$ \begin{pmatrix}7x+1y+2z \\ 1x+8y+3z \\ 2x+3y+9z\end{pmatrix}=$ \begin{pmatrix}10 \\ 8 \\ 6 \end{pmatrix}
6.1 ヤコビ法
代数的手法と比較すると早く値に近づいている。
近似後は近似値付近の増減(振動)を繰り返していた。
具体的数値だと更新が5回の時点で落ち着いてきており、総計は20回なのでなかなかに優秀であるといえる。
プログラムのソースコードをリスト7.1として以下に添付する。
https://gyazo.com/051fffb6235ddcbce257aa2e19530790
図1.ヤコビ法での近似推移
code:jacobi.py
import random
import math
import matplotlib.pyplot as plt
eps = 0.000001
num = []
array = []
ans = []
ct =0
if int(input("Homework => 1 \nRandom =>0 \n===>")) == 0:
retu = int(input("How long RETU?"))
result = []
for i in range(retu):
result.append(0)
ans.append(random.randint(1,10))
for j in range(retu):
array = []
for i in range(retu):
array.append(random.randint(1,10))
num.append(array)
else:
retu = 3
num = 7,1,2],1,8,3,[2,3,9 print("Question")
for i in range(retu):
plot_y = []
flag =0
while flag == 0:
flag = 1
Bresult = []
if ("inf" in str(result)) or ("-inf" in str(result)):
print("Unfortunately, this equation didn't seem to converge because it doesn't seem to be dominant diagonal.I can't get results. ")
exit()
for i in range(len(result)):
for i in range(len(result)):
for l in range(len(result)):
if i != l:
Bresulti = Bresulti - ((numil*resultl)/numii) ct = ct + 1
for i in range(len(result)):
resulti = abs(Bresulti - resulti) flag = 0
break
for i in range(len(result)):
print(result)
print("Answer")
for i in range(retu):
print('x_' + str(i) +'=' +str(resulti)) print("calculation ct = " + str(ct))
print(plot_y)
for i in range(len(result)):
for l in range(int(ct/len(result))):
print(l)
print(plot_result)
plt.plot(list(range(int(ct/len(result))+1)),plot_result,marker=plot_markeri,color = plot_colori,linestyle = plot_linestylei) plt.legend()
plt.show()
6.2 ガウス・ザイデル法
収束がヤコビ法と比較すると早い。
近似自体は特に早く、グラフのほとんどが近似値での振動であった。
具体的数値だと更新が1~2回の時点でかなり近似しており、総計も8回なのでかなり優秀であるといえる。
プログラムのソースコードをリスト7.2として以下に添付する。
https://gyazo.com/77388f895d30a4b6c12dda992642bfe4
図2.ガウス・ザイデル法での近似推移
code:seidel.py
import random
import math
import matplotlib.pyplot as plt
eps = 0.000001
num = []
array = []
ans = []
ct =0
if int(input("Homework => 1 \nRandom =>0 \n===>")) == 0:
retu = int(input("How long RETU?"))
result = []
for i in range(retu):
result.append(0)
ans.append(random.randint(1,10))
for j in range(retu):
array = []
for i in range(retu):
array.append(random.randint(1,10))
num.append(array)
else:
retu = 3
num = 7,1,2],1,8,3,[2,3,9 print("Question")
for i in range(retu):
plot_y = []
flag =0
while flag == 0:
flag = 1
Bresult = []
if ("inf" in str(result)) or ("-inf" in str(result)):
print("Unfortunately, this equation didn't seem to converge because it doesn't seem to be dominant diagonal.I can't get results. ")
exit()
for i in range(len(result)):
for i in range(len(result)):
for l in range(len(result)):
if i != l:
resulti = resulti - ((numil*resultl)/numii) ct = ct + 1
for i in range(len(result)):
Bresulti = abs(Bresulti - resulti) flag = 0
break
print(result)
print("Answer")
for i in range(retu):
print('x_' + str(i) +'=' +str(resulti)) print("calculation ct = " + str(ct))
print(plot_y)
for i in range(len(result)):
for l in range(int(ct/len(result))):
print(l)
print(plot_result)
plt.plot(list(range(int(ct/len(result))+1)),plot_result,marker=plot_markeri,color = plot_colori,linestyle = plot_linestylei) plt.legend()
plt.show()
https://gyazo.com/4031bb41108a1756d5556efc02346660記述日:2020年10月26日22:10https://gyazo.com/0902b55512d817b36596c44228e2d840 https://twitter.com/intent/tweet?text=%E3%81%B2%E3%82%86%E3%81%86%E3%81%AF%E3%81%98%E3%82%81%E3%81%95%E3%82%93%E3%81%AEScrapbox%E3%81%AE%E8%A8%98%E4%BA%8B%E3%82%92%E5%85%B1%E6%9C%89%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F%E3%80%82%0D%0Ahttps://scrapbox.io/hiyu-hajime/%25E5%258F%258D%25E5%25BE%25A9%25E6%25B3%2595%25E3%2582%2592%25E7%2594%25A8%25E3%2581%2584%25E3%2581%259F%25E9%2580%25A3%25E7%25AB%258B%25E6%2596%25B9%25E7%25A8%258B%25E5%25BC%258F%25E3%2581%25AE%25E8%25A7%25A3%25E6%25B3%2595