/***************************************/
/* 多次元ニュートン法 */
/* (3点(0.5,1.0),(0.0,1.5),(0.5,2.0) */
/* を通る円の中心と半径) */
/* coded by Y.Suganuma */
/***************************************/
#include <stdio.h>
int newton(void(*)(double *, double *), int(*)(double *, double **, double),
double *, double, double, int, double **, int, double *);
void snx(double *, double *);
int dsnx(double *, double **, double);
int gauss(double **, int, int, double);
int main()
{
double eps1, eps2, x[3], x0[3], **w;
int i1, max, ind;
eps1 = 1.0e-7;
eps2 = 1.0e-10;
max = 20;
x0[0] = 0.0;
x0[1] = 0.0;
x0[2] = 2.0;
w = new double * [3];
for (i1 = 0; i1 < 3; i1++)
w[i1] = new double [6];
ind = newton(snx, dsnx, x0, eps1, eps2, max, w, 3, x);
printf("ind = %d\n", ind);
printf("x");
for (i1 = 0; i1 < 3; i1++)
printf(" %f", x[i1]);
printf("\nf ");
snx(x, x0);
for (i1 = 0; i1 < 3; i1++)
printf(" %f", x0[i1]);
printf("\n");
for (i1 = 0; i1 < 3; i1++)
delete [] w[i1];
delete [] w;
return 0;
}
/*****************************************************/
/* Newton法による非線形方程式(f(x)=0)の解 */
/* fn : f(x)を計算する関数名 */
/* dfn : f(x)の微分を計算する関数名 */
/* 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 : 解 */
/* return : 実際の試行回数 */
/* (負の時は解を得ることができなかった) */
/*****************************************************/
#include <math.h>
int newton(void(*f)(double *, double *), int(*df)(double *, double **, double),
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++;
(*f)(x1, g);
sw1 = 0;
for (i1 = 0; i1 < n && sw1 == 0; i1++) {
if (fabs(g[i1]) > eps2)
sw1 = 1;
}
if (sw1 > 0) {
if (ind <= max) {
sw1 = (*df)(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 (fabs(x[i1]-x1[i1]) > eps1 && fabs(x[i1]-x1[i1]) > eps1*fabs(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;
}
}
delete [] g;
delete [] x1;
return ind;
}
/************************/
/* 関数値(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];
}
/*****************************************/
/* 関数の微分の計算 */
/* x : 変数値 */
/* w : 微分の逆行列(後半) */
/* eps : 逆行列の存在を判定する規準 */
/* return : =0 : 正常 */
/* =1 : 逆行列が存在しない */
/*****************************************/
int dsnx(double *x, double **w, double eps)
{
int i1, i2, sw;
for (i1 = 0; i1 < 3; i1++) {
for (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];
sw = gauss(w, 3, 3, eps);
return sw;
}
/*******************************************************/
/* 線形連立方程式を解く(逆行列を求める) */
/* w : 方程式の左辺及び右辺 */
/* n : 方程式の数 */
/* m : 方程式の右辺の列の数 */
/* eps : 逆行列の存在を判定する規準 */
/* return : =0 : 正常 */
/* =1 : 逆行列が存在しない */
/*******************************************************/
#include <math.h>
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 = fabs(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);
}