############################################
# 多次元ニュートン法
# (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")