1 文本格式
using System;
namespace Legalsoft.Truffer
{
/// <summary>
/// Computes all eigenvalues and eigenvectors of a real symmetric matrix by
/// reduction to tridiagonal form followed by QL iteration.
/// </summary>
public class Symmeig
{
public int n { get; set; }
public double[,] z;
public double[] d;
public double[] e;
public bool yesvecs { get; set; }
/// <summary>
/// Computes all eigenvalues and eigenvectors of a real symmetric matrix
/// a[0..n - 1][0..n - 1] by reduction to tridiagonal form followed by QL
/// iteration.On output, d[0..n - 1] contains the eigenvalues of a sorted into
/// descending order, while z[0..n - 1][0..n - 1] is a matrix whose columns contain
/// the corresponding normalized eigenvectors.If yesvecs is input as true (the
/// default), then the eigenvectors are computed.If yesvecs is input as false,
/// only the eigenvalues are computed.
/// </summary>
/// <param name="a"></param>
/// <param name="yesvec"></param>
public Symmeig(double[,] a, bool yesvec = true)
{
this.n = a.GetLength(0);
this.z = a;
this.d = new double[n];
this.e = new double[n];
this.yesvecs = yesvec;
tred2();
tqli();
sort();
}
/// <summary>
/// Computes all eigenvalues and(optionally) eigenvectors of a real,
/// symmetric, tridiagonal matrix by QL iteration.On input, dd[0..n - 1]
/// contains the diagonal elements of the tridi- agonal matrix.The vector
/// ee[0..n-1] inputs the subdiagonal elements of the tridiagonal matrix, with
/// ee[0] arbitrary.Output is the same as the constructor above.
/// </summary>
/// <param name="dd"></param>
/// <param name="ee"></param>
/// <param name="yesvec"></param>
public Symmeig(double[] dd, double[] ee, bool yesvec = true)
{
this.n = dd.Length;
this.d = dd;
this.e = ee;
this.z = new double[n, n];
this.yesvecs = yesvec;
for (int i = 0; i < n; i++)
{
z[i, i] = 1.0;
}
tqli();
sort();
}
public void sort()
{
if (yesvecs)
{
Jacobi.eigsrt( d, z);
}
else
{
Jacobi.eigsrt( d, z);
}
}
/// <summary>
/// Householder reduction of a real symmetric matrix z[0..n - 1][0..n-1]. (The
/// input matrix A to Symmeig is stored in z.) On output, z is replaced by the
/// orthogonal matrix Q effecting the transformation.d[0..n - 1] contains the
/// diagonal elements of the tridiagonal matrix and e[0..n - 1] the off-diagonal
/// elements, with e[0]=0. If yesvecs is false, so that only eigenvalues will
/// subsequently be determined, several statements are omitted, in which case z
/// contains no useful information on output.
/// </summary>
public void tred2()
{
for (int i = n - 1; i > 0; i--)
{
int l = i - 1;
double h = 0.0;
double scale = 0.0;
if (l > 0)
{
for (int k = 0; k < i; k++)
{
scale += Math.Abs(z[i, k]);
}
//if (scale == 0.0)
if (Math.Abs(scale) <= float.Epsilon)
{
e[i] = z[i, l];
}
else
{
for (int k = 0; k < i; k++)
{
z[i, k] /= scale;
h += z[i, k] * z[i, k];
}
double f = z[i, l];
double g = (f >= 0.0 ? -Math.Sqrt(h) : Math.Sqrt(h));
e[i] = scale * g;
h -= f * g;
z[i, l] = f - g;
f = 0.0;
for (int j = 0; j < i; j++)
{
if (yesvecs)
{
z[j, i] = z[i, j] / h;
}
g = 0.0;
for (int k = 0; k < j + 1; k++)
{
g += z[j, k] * z[i, k];
}
for (int k = j + 1; k < i; k++)
{
g += z[k, j] * z[i, k];
}
e[j] = g / h;
f += e[j] * z[i, j];
}
double hh = f / (h + h);
for (int j = 0; j < i; j++)
{
f = z[i, j];
e[j] = g = e[j] - hh * f;
for (int k = 0; k < j + 1; k++)
{
z[j, k] -= (f * e[k] + g * z[i, k]);
}
}
}
}
else
{
e[i] = z[i, l];
}
d[i] = h;
}
if (yesvecs)
{
d[0] = 0.0;
}
e[0] = 0.0;
for (int i = 0; i < n; i++)
{
if (yesvecs)
{
if (d[i] != 0.0)
{
for (int j = 0; j < i; j++)
{
double g = 0.0;
for (int k = 0; k < i; k++)
{
g += z[i, k] * z[k, j];
}
for (int k = 0; k < i; k++)
{
z[k, j] -= g * z[k, i];
}
}
}
d[i] = z[i, i];
z[i, i] = 1.0;
for (int j = 0; j < i; j++)
{
z[j, i] = z[i, j] = 0.0;
}
}
else
{
d[i] = z[i, i];
}
}
}
/// <summary>
/// QL algorithm with implicit shifts to determine the eigenvalues and
/// (optionally) the eigenvectors of a real, symmetric, tridiagonal matrix, or
/// of a real symmetric matrix previously reduced by tred2. On input,
/// d[0..n-1] contains the diagonal elements of the tridiagonal matrix. On
/// output, it returns the eigenvalues. The vector e[0..n - 1] inputs the
/// subdiagonal elements of the tridiagonal matrix, with e[0] arbitrary. On
/// output e is destroyed.If the eigenvectors of a tridiagonal matrix are
/// desired, the matrix z[0..n - 1][0..n - 1] is input as the identity matrix.If
/// the eigenvectors of a matrix that has been reduced by tred2 are required,
/// then z is input as the matrix output by tred2.In either case, column k of
/// z returns the normalized eigenvector corresponding to d[k]
/// </summary>
/// <exception cref="Exception"></exception>
public void tqli()
{
const double EPS = float.Epsilon;
for (int i = 1; i < n; i++)
{
e[i - 1] = e[i];
}
e[n - 1] = 0.0;
for (int l = 0; l < n; l++)
{
int iter = 0;
int m;
do
{
for (m = l; m < n - 1; m++)
{
double dd = Math.Abs(d[m]) + Math.Abs(d[m + 1]);
if (Math.Abs(e[m]) <= EPS * dd)
{
break;
}
}
if (m != l)
{
if (iter++ == 30)
{
throw new Exception("Too many iterations in tqli");
}
double g = (d[l + 1] - d[l]) / (2.0 * e[l]);
double r = pythag(g, 1.0);
g = d[m] - d[l] + e[l] / (g + Globals.SIGN(r, g));
double s = 1.0;
double c = 1.0;
double p = 0.0;
int i = m - 1;
for (; i >= l; i--)
{
double f = s * e[i];
double b = c * e[i];
e[i + 1] = (r = pythag(f, g));
//if (r == 0.0)
if (Math.Abs(r) <= float.Epsilon)
{
d[i + 1] -= p;
e[m] = 0.0;
break;
}
s = f / r;
c = g / r;
g = d[i + 1] - p;
r = (d[i] - g) * s + 2.0 * c * b;
d[i + 1] = g + (p = s * r);
g = c * r - b;
if (yesvecs)
{
for (int k = 0; k < n; k++)
{
f = z[k, i + 1];
z[k, i + 1] = s * z[k, i] + c * f;
z[k, i] = c * z[k, i] - s * f;
}
}
}
//if (r == 0.0 && i >= l)
if (Math.Abs(r) <= float.Epsilon && i >= l)
{
continue;
}
d[l] -= p;
e[l] = g;
e[m] = 0.0;
}
} while (m != l);
}
}
public double pythag(double a, double b)
{
double absa = Math.Abs(a);
double absb = Math.Abs(b);
//return (absa > absb ? absa * Math.Sqrt(1.0 + Globals.SQR(absb / absa)) : (absb == 0.0 ? 0.0 : absb * Math.Sqrt(1.0 + Globals.SQR(absa / absb))));
return (absa > absb ? absa * Math.Sqrt(1.0 + Globals.SQR(absb / absa)) : ((absb <= float.Epsilon) ? 0.0 : absb * Math.Sqrt(1.0 + Globals.SQR(absa / absb))));
}
}
}
2 代码格式
using System;
namespace Legalsoft.Truffer
{
/// <summary>
/// Computes all eigenvalues and eigenvectors of a real symmetric matrix by
/// reduction to tridiagonal form followed by QL iteration.
/// </summary>
public class Symmeig
{
public int n { get; set; }
public double[,] z;
public double[] d;
public double[] e;
public bool yesvecs { get; set; }
/// <summary>
/// Computes all eigenvalues and eigenvectors of a real symmetric matrix
/// a[0..n - 1][0..n - 1] by reduction to tridiagonal form followed by QL
/// iteration.On output, d[0..n - 1] contains the eigenvalues of a sorted into
/// descending order, while z[0..n - 1][0..n - 1] is a matrix whose columns contain
/// the corresponding normalized eigenvectors.If yesvecs is input as true (the
/// default), then the eigenvectors are computed.If yesvecs is input as false,
/// only the eigenvalues are computed.
/// </summary>
/// <param name="a"></param>
/// <param name="yesvec"></param>
public Symmeig(double[,] a, bool yesvec = true)
{
this.n = a.GetLength(0);
this.z = a;
this.d = new double[n];
this.e = new double[n];
this.yesvecs = yesvec;
tred2();
tqli();
sort();
}
/// <summary>
/// Computes all eigenvalues and(optionally) eigenvectors of a real,
/// symmetric, tridiagonal matrix by QL iteration.On input, dd[0..n - 1]
/// contains the diagonal elements of the tridi- agonal matrix.The vector
/// ee[0..n-1] inputs the subdiagonal elements of the tridiagonal matrix, with
/// ee[0] arbitrary.Output is the same as the constructor above.
/// </summary>
/// <param name="dd"></param>
/// <param name="ee"></param>
/// <param name="yesvec"></param>
public Symmeig(double[] dd, double[] ee, bool yesvec = true)
{
this.n = dd.Length;
this.d = dd;
this.e = ee;
this.z = new double[n, n];
this.yesvecs = yesvec;
for (int i = 0; i < n; i++)
{
z[i, i] = 1.0;
}
tqli();
sort();
}
public void sort()
{
if (yesvecs)
{
Jacobi.eigsrt( d, z);
}
else
{
Jacobi.eigsrt( d, z);
}
}
/// <summary>
/// Householder reduction of a real symmetric matrix z[0..n - 1][0..n-1]. (The
/// input matrix A to Symmeig is stored in z.) On output, z is replaced by the
/// orthogonal matrix Q effecting the transformation.d[0..n - 1] contains the
/// diagonal elements of the tridiagonal matrix and e[0..n - 1] the off-diagonal
/// elements, with e[0]=0. If yesvecs is false, so that only eigenvalues will
/// subsequently be determined, several statements are omitted, in which case z
/// contains no useful information on output.
/// </summary>
public void tred2()
{
for (int i = n - 1; i > 0; i--)
{
int l = i - 1;
double h = 0.0;
double scale = 0.0;
if (l > 0)
{
for (int k = 0; k < i; k++)
{
scale += Math.Abs(z[i, k]);
}
//if (scale == 0.0)
if (Math.Abs(scale) <= float.Epsilon)
{
e[i] = z[i, l];
}
else
{
for (int k = 0; k < i; k++)
{
z[i, k] /= scale;
h += z[i, k] * z[i, k];
}
double f = z[i, l];
double g = (f >= 0.0 ? -Math.Sqrt(h) : Math.Sqrt(h));
e[i] = scale * g;
h -= f * g;
z[i, l] = f - g;
f = 0.0;
for (int j = 0; j < i; j++)
{
if (yesvecs)
{
z[j, i] = z[i, j] / h;
}
g = 0.0;
for (int k = 0; k < j + 1; k++)
{
g += z[j, k] * z[i, k];
}
for (int k = j + 1; k < i; k++)
{
g += z[k, j] * z[i, k];
}
e[j] = g / h;
f += e[j] * z[i, j];
}
double hh = f / (h + h);
for (int j = 0; j < i; j++)
{
f = z[i, j];
e[j] = g = e[j] - hh * f;
for (int k = 0; k < j + 1; k++)
{
z[j, k] -= (f * e[k] + g * z[i, k]);
}
}
}
}
else
{
e[i] = z[i, l];
}
d[i] = h;
}
if (yesvecs)
{
d[0] = 0.0;
}
e[0] = 0.0;
for (int i = 0; i < n; i++)
{
if (yesvecs)
{
if (d[i] != 0.0)
{
for (int j = 0; j < i; j++)
{
double g = 0.0;
for (int k = 0; k < i; k++)
{
g += z[i, k] * z[k, j];
}
for (int k = 0; k < i; k++)
{
z[k, j] -= g * z[k, i];
}
}
}
d[i] = z[i, i];
z[i, i] = 1.0;
for (int j = 0; j < i; j++)
{
z[j, i] = z[i, j] = 0.0;
}
}
else
{
d[i] = z[i, i];
}
}
}
/// <summary>
/// QL algorithm with implicit shifts to determine the eigenvalues and
/// (optionally) the eigenvectors of a real, symmetric, tridiagonal matrix, or
/// of a real symmetric matrix previously reduced by tred2. On input,
/// d[0..n-1] contains the diagonal elements of the tridiagonal matrix. On
/// output, it returns the eigenvalues. The vector e[0..n - 1] inputs the
/// subdiagonal elements of the tridiagonal matrix, with e[0] arbitrary. On
/// output e is destroyed.If the eigenvectors of a tridiagonal matrix are
/// desired, the matrix z[0..n - 1][0..n - 1] is input as the identity matrix.If
/// the eigenvectors of a matrix that has been reduced by tred2 are required,
/// then z is input as the matrix output by tred2.In either case, column k of
/// z returns the normalized eigenvector corresponding to d[k]
/// </summary>
/// <exception cref="Exception"></exception>
public void tqli()
{
const double EPS = float.Epsilon;
for (int i = 1; i < n; i++)
{
e[i - 1] = e[i];
}
e[n - 1] = 0.0;
for (int l = 0; l < n; l++)
{
int iter = 0;
int m;
do
{
for (m = l; m < n - 1; m++)
{
double dd = Math.Abs(d[m]) + Math.Abs(d[m + 1]);
if (Math.Abs(e[m]) <= EPS * dd)
{
break;
}
}
if (m != l)
{
if (iter++ == 30)
{
throw new Exception("Too many iterations in tqli");
}
double g = (d[l + 1] - d[l]) / (2.0 * e[l]);
double r = pythag(g, 1.0);
g = d[m] - d[l] + e[l] / (g + Globals.SIGN(r, g));
double s = 1.0;
double c = 1.0;
double p = 0.0;
int i = m - 1;
for (; i >= l; i--)
{
double f = s * e[i];
double b = c * e[i];
e[i + 1] = (r = pythag(f, g));
//if (r == 0.0)
if (Math.Abs(r) <= float.Epsilon)
{
d[i + 1] -= p;
e[m] = 0.0;
break;
}
s = f / r;
c = g / r;
g = d[i + 1] - p;
r = (d[i] - g) * s + 2.0 * c * b;
d[i + 1] = g + (p = s * r);
g = c * r - b;
if (yesvecs)
{
for (int k = 0; k < n; k++)
{
f = z[k, i + 1];
z[k, i + 1] = s * z[k, i] + c * f;
z[k, i] = c * z[k, i] - s * f;
}
}
}
//if (r == 0.0 && i >= l)
if (Math.Abs(r) <= float.Epsilon && i >= l)
{
continue;
}
d[l] -= p;
e[l] = g;
e[m] = 0.0;
}
} while (m != l);
}
}
public double pythag(double a, double b)
{
double absa = Math.Abs(a);
double absb = Math.Abs(b);
//return (absa > absb ? absa * Math.Sqrt(1.0 + Globals.SQR(absb / absa)) : (absb == 0.0 ? 0.0 : absb * Math.Sqrt(1.0 + Globals.SQR(absa / absb))));
return (absa > absb ? absa * Math.Sqrt(1.0 + Globals.SQR(absb / absa)) : ((absb <= float.Epsilon) ? 0.0 : absb * Math.Sqrt(1.0 + Globals.SQR(absa / absb))));
}
}
}