C#,数值计算——多维拟牛顿或变度量方法(Quasi-Newton or Variable Metric Methods in Multidimensions)的计算方法与源程序
1 Quasi-Newton (Variable Metric) Methods
Quasi-Newton methods attempt to generate an estimate of the inverse of the Hessian matrix
. This is then used to determine the next iteration point.
The gradient in the region around a is given by
At the minima the gradient is zero
thus the best next direction step is given by
The BFGS (`Broyden-Fletcher-Goldfarb-Shanno') algorithm is based on this technique.
2 C#源程序
using System;
namespace Legalsoft.Truffer
{
/// <summary>
/// Quasi-Newton or Variable Metric Methods in Multidimensions
/// </summary>
public class QuasiNewton
{
public static void dfpmin(double[] p, double gtol, ref int iter, ref double fret, RealValueFunWithDiff funcd)
{
const int ITMAX = 200;
const double EPS = float.Epsilon; //numeric_limits<double>.epsilon();
const double TOLX = 4 * EPS;
const double STPMX = 100.0;
bool check = false;
int n = p.Length;
double[] dg = new double[n];
double[] g = new double[n];
double[] hdg = new double[n];
double[] pnew = new double[n];
double[] xi = new double[n];
double[,] hessin = new double[n, n];
double fp = funcd.funk(p);
funcd.df(p,g);
double sum = 0.0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
hessin[i, j] = 0.0;
}
hessin[i, i] = 1.0;
xi[i] = -g[i];
sum += p[i] * p[i];
}
double stpmax = STPMX * Math.Max(Math.Sqrt(sum), (double)n);
for (int its = 0; its < ITMAX; its++)
{
iter = its;
Roots.lnsrch(p, fp, g, xi, pnew, ref fret, stpmax, ref check, funcd);
fp = fret;
for (int i = 0; i < n; i++)
{
xi[i] = pnew[i] - p[i];
p[i] = pnew[i];
}
double test = 0.0;
for (int i = 0; i < n; i++)
{
double temp = Math.Abs(xi[i]) / Math.Max(Math.Abs(p[i]), 1.0);
if (temp > test)
{
test = temp;
}
}
if (test < TOLX)
{
return;
}
for (int i = 0; i < n; i++)
{
dg[i] = g[i];
}
funcd.df(p,g);
test = 0.0;
double den = Math.Max(Math.Abs(fret), 1.0);
for (int i = 0; i < n; i++)
{
double temp = Math.Abs(g[i]) * Math.Max(Math.Abs(p[i]), 1.0) / den;
if (temp > test)
{
test = temp;
}
}
if (test < gtol)
{
return;
}
for (int i = 0; i < n; i++)
{
dg[i] = g[i] - dg[i];
}
for (int i = 0; i < n; i++)
{
hdg[i] = 0.0;
for (int j = 0; j < n; j++)
{
hdg[i] += hessin[i, j] * dg[j];
}
}
double fac = 0.0;
double fae = 0.0;
double sumdg = 0.0;
double sumxi = 0.0;
for (int i = 0; i < n; i++)
{
fac += dg[i] * xi[i];
fae += dg[i] * hdg[i];
sumdg += Globals.SQR(dg[i]);
sumxi += Globals.SQR(xi[i]);
}
if (fac > Math.Sqrt(EPS * sumdg * sumxi))
{
fac = 1.0 / fac;
double fad = 1.0 / fae;
for (int i = 0; i < n; i++)
{
dg[i] = fac * xi[i] - fad * hdg[i];
}
for (int i = 0; i < n; i++)
{
for (int j = i; j < n; j++)
{
hessin[i, j] += fac * xi[i] * xi[j] - fad * hdg[i] * hdg[j] + fae * dg[i] * dg[j];
hessin[j, i] = hessin[i, j];
}
}
}
for (int i = 0; i < n; i++)
{
xi[i] = 0.0;
for (int j = 0; j < n; j++)
{
xi[i] -= hessin[i, j] * g[j];
}
}
}
throw new Exception("too many iterations in dfpmin");
}
}
}