############################################ # 多次元ニュートン法 # (3点(0.5,1.0),(0.0,1.5),(0.5,2.0) # を通る円の中心と半径) # coded by Y.Suganuma ############################################ ############################################ # Newton法による非線形方程式(f(x)=0)の解 # x0 : 初期値 # eps1 : 終了条件1(|x(k+1)-x(k)|<eps1) # eps2 : 終了条件2(|f(x(k))|<eps2) # max : 最大試行回数 # w : f(x)の微分の逆行列を入れる (n x 2n) # n : xの次数 # x : 解 # fn : f(x)とその微分を計算する関数名 # return : 実際の試行回数 # (負の時は解を得ることができなかった) # coded by Y.Suganuma ############################################ def newton(x0, eps1, eps2, max, w, n, x, &fn) ind = 0 sw = 0 g = Array.new() x1 = Array.new() for i1 in 0 ... n x1[i1] = x0[i1] x[i1] = x1[i1] end while sw == 0 && ind >= 0 sw = 1 ind += 1 fn.call(0, x1, g, w, eps2) sw1 = 0 for i1 in 0 ... n if g[i1].abs() > eps2 sw1 = 1 break end end if sw1 > 0 if ind <= max sw1 = fn.call(1, x1, g, w, eps2) if sw1 == 0 for i1 in 0 ... n x[i1] = x1[i1] for i2 in 0 ... n x[i1] -= w[i1][i2+n] * g[i2] end end sw1 = 0 for i1 in 0 ... n if (x[i1]-x1[i1]).abs() > eps1 && (x[i1]-x1[i1]).abs() > eps1*x[i1].abs() sw1 = 1 break end end if sw1 > 0 for i1 in 0 ... n x1[i1] = x[i1] end sw = 0 end else ind = -1 end else ind = -1 end end end return ind end ############################################ # 線形連立方程式を解く(逆行列を求める) # w : 方程式の左辺及び右辺 # n : 方程式の数 # m : 方程式の右辺の列の数 # eps : 逆行列の存在を判定する規準 # return : =0 : 正常 # =1 : 逆行列が存在しない # coded by Y.Suganuma ############################################ def gauss(w, n, m, eps) nm = n + m ind = 0 for i1 in 0 ... n y1 = 0.0 m1 = i1 + 1 m2 = 0 # ピボット要素の選択 for i2 in i1 ... n y2 = w[i2][i1].abs() if y1 < y2 y1 = y2 m2 = i2 end end # 逆行列が存在しない if y1 < eps ind = 1 break # 逆行列が存在する else # 行の入れ替え for i2 in i1 ... nm y1 = w[i1][i2] w[i1][i2] = w[m2][i2] w[m2][i2] = y1 end # 掃き出し操作 y1 = 1.0 / w[i1][i1] for i2 in m1 ... nm w[i1][i2] *= y1 end for i2 in 0 ... n if i2 != i1 for i3 in m1 ... nm w[i2][i3] -= (w[i2][i1] * w[i1][i3]) end end end end end return ind end ######################################## # 関数値(f(x))とその微分の計算 # x : 変数値 # y : 関数値 # w : 微分の逆行列(後半) # eps : 逆行列の存在を判定する規準 # return : =0 : 正常 # =1 : 逆行列が存在しない ######################################## snx = Proc.new { |sw, x, y, w, eps| if sw == 0 # 関数値 y[0] = (0.5 - x[0]) * (0.5 - x[0]) + (1.0 - x[1]) * (1.0 - x[1]) - x[2] * x[2]; y[1] = (0.0 - x[0]) * (0.0 - x[0]) + (1.5 - x[1]) * (1.5 - x[1]) - x[2] * x[2]; y[2] = (0.5 - x[0]) * (0.5 - x[0]) + (2.0 - x[1]) * (2.0 - x[1]) - x[2] * x[2]; else # 微分 for i1 in 0 ... 3 for i2 in 0 ... 3 w[i1][i2+3] = 0.0 end w[i1][i1+3] = 1.0 end w[0][0] = -2.0 * (0.5 - x[0]) w[0][1] = -2.0 * (1.0 - x[1]) w[0][2] = -2.0 * x[2] w[1][0] = -2.0 * (0.0 - x[0]) w[1][1] = -2.0 * (1.5 - x[1]) w[1][2] = -2.0 * x[2] w[2][0] = -2.0 * (0.5 - x[0]) w[2][1] = -2.0 * (2.0 - x[1]) w[2][2] = -2.0 * x[2] sw = gauss(w, 3, 3, eps) end } # データの設定 eps1 = 1.0e-7 eps2 = 1.0e-10 max = 20 n = 3 x = [0, 0, 0] x0 = [0, 0, 2] w = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]] # 実行と結果 ind = newton(x0, eps1, eps2, max, w, n, x, &snx) print("ind = ", ind, "\n") print("x ", x[0], " ", x[1], " ", x[2], "\n") snx.call(0, x, x0, w, eps1) print("f ", x0[0], " ", x0[1], " ", x0[2], "\n")