/***************************************/
/* 多次元ニュートン法 */
/* (3点(0.5,1.0),(0.0,1.5),(0.5,2.0) */
/* を通る円の中心と半径) */
/* coded by Y.Suganuma */
/***************************************/
import java.io.*;
public class Test {
public static void main(String args[]) throws IOException
{
double eps1, eps2, x[], x0[], w[][];
int i1, max, ind;
x = new double [3];
x0 = new double [3];
w = new double [3][6];
eps1 = 1.0e-7;
eps2 = 1.0e-10;
max = 20;
x0[0] = 0.0;
x0[1] = 0.0;
x0[2] = 2.0;
Kansu kn = new Kansu();
ind = kn.newton_m(x0, eps1, eps2, max, w, 3, x);
System.out.println("ind = " + ind);
System.out.print("x");
for (i1 = 0; i1 < 3; i1++)
System.out.print(" " + x[i1]);
System.out.println("\n");
kn.snx(x, x0);
for (i1 = 0; i1 < 3; i1++)
System.out.print(" " + x0[i1]);
System.out.println();
}
}
/******************************/
/* 関数値およびその微分の計算 */
/******************************/
class Kansu extends Newton
{
// 関数値(f(x))の計算
void snx(double x[], double y[])
{
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];
}
// 関数の微分の計算
int dsnx(double x[], double w[][], double eps)
{
for (int i1 = 0; i1 < 3; i1++) {
for (int i2 = 0; i2 < 3; i2++)
w[i1][i2+3] = 0.0;
w[i1][i1+3] = 1.0;
}
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];
int ind = gauss(w, 3, 3, eps);
return ind;
}
}
abstract class Newton
{
/*****************************************************/
/* Newton法による多次元非線形方程式(f(x)=0)の解 */
/* x1 : 初期値 */
/* eps1 : 終了条件1(|x(k+1)-x(k)|<eps1) */
/* eps2 : 終了条件2(|f(x(k))|<eps2) */
/* max : 最大試行回数 */
/* kn1 : 関数を計算するクラスオブジェクト */
/* kn2 : 関数の微分を計算するクラスオブジェクト */
/* w : f(x)の微分の逆行列を入れる (n x 2n) */
/* n : xの次数 */
/* x : 解 */
/* return : 実際の試行回数 */
/* (負の時は解を得ることができなかった) */
/*****************************************************/
abstract void snx(double x[], double y[]);
abstract int dsnx(double x[], double w[][], double eps);
int newton_m(double x0[], double eps1, double eps2, int max, double w[][], int n, double x[])
{
double g[], x1[];
int i1, i2, ind = 0, sw, sw1;
g = new double [n];
x1 = new double [n];
sw = 0;
for (i1 = 0; i1 < n; i1++) {
x1[i1] = x0[i1];
x[i1] = x1[i1];
}
while (sw == 0 && ind >= 0) {
sw = 1;
ind++;
snx(x1, g);
sw1 = 0;
for (i1 = 0; i1 < n && sw1 == 0; i1++) {
if (Math.abs(g[i1]) > eps2)
sw1 = 1;
}
if (sw1 > 0) {
if (ind <= max) {
sw1 = dsnx(x1, w, eps2);
if (sw1 == 0) {
for (i1 = 0; i1 < n; i1++) {
x[i1] = x1[i1];
for (i2 = 0; i2 < n; i2++)
x[i1] -= w[i1][i2+n] * g[i2];
}
sw1 = 0;
for (i1 = 0; i1 < n && sw1 == 0; i1++) {
if (Math.abs(x[i1]-x1[i1]) > eps1 &&
Math.abs(x[i1]-x1[i1]) > eps1*Math.abs(x[i1]))
sw1 = 1;
}
if (sw1 > 0) {
for (i1 = 0; i1 < n; i1++)
x1[i1] = x[i1];
sw = 0;
}
}
else
ind = -1;
}
else
ind = -1;
}
}
return ind;
}
/*******************************************************/
/* 線形連立方程式を解く(逆行列を求める) */
/* w : 方程式の左辺及び右辺 */
/* n : 方程式の数 */
/* m : 方程式の右辺の列の数 */
/* eps : 逆行列の存在を判定する規準 */
/* return : =0 : 正常 */
/* =1 : 逆行列が存在しない */
/*******************************************************/
int gauss(double w[][], int n, int m, double eps)
{
double y1, y2;
int ind = 0, nm, m1, m2, i1, i2, i3;
nm = n + m;
for (i1 = 0; i1 < n && ind == 0; i1++) {
y1 = .0;
m1 = i1 + 1;
m2 = 0;
// ピボット要素の選択
for (i2 = i1; i2 < n; i2++) {
y2 = Math.abs(w[i2][i1]);
if (y1 < y2) {
y1 = y2;
m2 = i2;
}
}
// 逆行列が存在しない
if (y1 < eps)
ind = 1;
// 逆行列が存在する
else {
// 行の入れ替え
for (i2 = i1; i2 < nm; i2++) {
y1 = w[i1][i2];
w[i1][i2] = w[m2][i2];
w[m2][i2] = y1;
}
// 掃き出し操作
y1 = 1.0 / w[i1][i1];
for (i2 = m1; i2 < nm; i2++)
w[i1][i2] *= y1;
for (i2 = 0; i2 < n; i2++) {
if (i2 != i1) {
for (i3 = m1; i3 < nm; i3++)
w[i2][i3] -= w[i2][i1] * w[i1][i3];
}
}
}
}
return(ind);
}
}