forked from tsai/mathnet-numerics
8 changed files with 242 additions and 4 deletions
@ -0,0 +1,48 @@ |
|||||
|
using System; |
||||
|
using MathNet.Numerics.Properties; |
||||
|
|
||||
|
namespace MathNet.Numerics.RootFinding |
||||
|
{ |
||||
|
public static class Bracketing |
||||
|
{ |
||||
|
/// <summary>Detect a range containing at least one root.</summary>
|
||||
|
/// <param name="f">The function to detect roots from.</param>
|
||||
|
/// <param name="xmin">Lower value of the range.</param>
|
||||
|
/// <param name="xmax">Upper value of the range</param>
|
||||
|
/// <param name="factor">The growing factor of research. Usually 1.6.</param>
|
||||
|
/// <param name="maxIterations">Maximum number of iterations. Usually 50.</param>
|
||||
|
/// <returns>True if the bracketing operation succeeded, false otherwise.</returns>
|
||||
|
/// <remarks>This iterative methods stops when two values with opposite signs are found.</remarks>
|
||||
|
public static bool SearchOutward(Func<double, double> f, ref double xmin, ref double xmax, double factor = 1.6, int maxIterations = 50) |
||||
|
{ |
||||
|
if (xmin >= xmax) |
||||
|
{ |
||||
|
throw new ArgumentOutOfRangeException("xmax", string.Format(Resources.ArgumentOutOfRangeGreater, "xmax", "xmin")); |
||||
|
} |
||||
|
|
||||
|
double fmin = f(xmin); |
||||
|
double fmax = f(xmax); |
||||
|
|
||||
|
for(int i=0;i<maxIterations; i++) |
||||
|
{ |
||||
|
if (Math.Sign(fmin) != Math.Sign(fmax)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (Math.Abs(fmin) < Math.Abs(fmax)) |
||||
|
{ |
||||
|
xmin += factor * (xmin - xmax); |
||||
|
fmin = f(xmin); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
xmax += factor * (xmax - xmin); |
||||
|
fmax = f(xmax); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,125 @@ |
|||||
|
using System; |
||||
|
using MathNet.Numerics.Properties; |
||||
|
|
||||
|
namespace MathNet.Numerics.RootFinding |
||||
|
{ |
||||
|
public static class FindRoots |
||||
|
{ |
||||
|
/// <summary>Find a solution of the equation f(x)=0.</summary>
|
||||
|
/// <param name="f">The function to find roots from.</param>
|
||||
|
/// <param name="xmin">The low value of the range where the root is supposed to be.</param>
|
||||
|
/// <param name="xmax">The high value of the range where the root is supposed to be.</param>
|
||||
|
/// <param name="accuracy">Desired accuracy. The root will be refined until the accuracy or the maximum number of iterations is reached.</param>
|
||||
|
/// <param name="maxIterations">Maximum number of iterations. Usually 100.</param>
|
||||
|
/// <returns>Returns the root with the specified accuracy.</returns>
|
||||
|
/// <remarks>
|
||||
|
/// Algorithm by by Brent, Van Wijngaarden, Dekker et al.
|
||||
|
/// Implementation inspired by Press, Teukolsky, Vetterling, and Flannery, "Numerical Recipes in C", 2nd edition, Cambridge University Press
|
||||
|
/// </remarks>
|
||||
|
public static double BrentMethod(Func<double, double> f, double xmin, double xmax, double accuracy = 1e-8, int maxIterations = 100) |
||||
|
{ |
||||
|
double xMid = 0; |
||||
|
double d = 0.0, e = 0.0; |
||||
|
|
||||
|
double fxmin = f(xmin); |
||||
|
double fxmax = f(xmax); |
||||
|
double root = xmax; |
||||
|
double froot = fxmax; |
||||
|
|
||||
|
for (int i = 0; i <= maxIterations; i++) |
||||
|
{ |
||||
|
// adjust bounds
|
||||
|
if (Math.Sign(froot) == Math.Sign(fxmax)) |
||||
|
{ |
||||
|
xmax = xmin; |
||||
|
fxmax = fxmin; |
||||
|
e = d = root - xmin; |
||||
|
} |
||||
|
|
||||
|
if (Math.Abs(fxmax) < Math.Abs(froot)) |
||||
|
{ |
||||
|
xmin = root; |
||||
|
root = xmax; |
||||
|
xmax = xmin; |
||||
|
fxmin = froot; |
||||
|
froot = fxmax; |
||||
|
fxmax = fxmin; |
||||
|
} |
||||
|
|
||||
|
// convergence check
|
||||
|
double xAcc1 = 2.0 * Precision.DoubleMachinePrecision * Math.Abs(root) + 0.5 * accuracy; |
||||
|
xMid = (xmax - root) / 2.0; |
||||
|
if (Math.Abs(xMid) <= xAcc1 || froot.AlmostEqualWithAbsoluteError(0, froot, accuracy)) |
||||
|
{ |
||||
|
return root; |
||||
|
} |
||||
|
|
||||
|
if (Math.Abs(e) >= xAcc1 && Math.Abs(fxmin) > Math.Abs(froot)) |
||||
|
{ |
||||
|
// Attempt inverse quadratic interpolation
|
||||
|
double s = froot / fxmin; |
||||
|
double p; |
||||
|
double q; |
||||
|
if (xmin.AlmostEqual(xmax)) |
||||
|
{ |
||||
|
p = 2.0 * xMid * s; |
||||
|
q = 1.0 - s; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
q = fxmin / fxmax; |
||||
|
double r = froot / fxmax; |
||||
|
p = s * (2.0 * xMid * q * (q - r) - (root - xmin) * (r - 1.0)); |
||||
|
q = (q - 1.0) * (r - 1.0) * (s - 1.0); |
||||
|
} |
||||
|
|
||||
|
if (p > 0.0) |
||||
|
{ |
||||
|
// Check whether in bounds
|
||||
|
q = -q; |
||||
|
} |
||||
|
p = Math.Abs(p); |
||||
|
if (2.0 * p < Math.Min(3.0 * xMid * q - Math.Abs(xAcc1 * q), Math.Abs(e * q))) |
||||
|
{ |
||||
|
// Accept interpolation
|
||||
|
e = d; |
||||
|
d = p / q; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Interpolation failed, use bisection
|
||||
|
d = xMid; |
||||
|
e = d; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Bounds decreasing too slowly, use bisection
|
||||
|
d = xMid; |
||||
|
e = d; |
||||
|
} |
||||
|
xmin = root; |
||||
|
fxmin = froot; |
||||
|
if (Math.Abs(d) > xAcc1) |
||||
|
{ |
||||
|
root += d; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
root += Sign(xAcc1, xMid); |
||||
|
} |
||||
|
froot = f(root); |
||||
|
} |
||||
|
|
||||
|
// The algorithm has exceeded the number of iterations allowed
|
||||
|
throw new RootFindingException(Resources.AccuracyNotReached, maxIterations, xmin, xmax, Math.Abs(xMid)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>Helper method useful for preventing rounding errors.</summary>
|
||||
|
/// <returns>a*sign(b)</returns>
|
||||
|
static double Sign(double a, double b) |
||||
|
{ |
||||
|
return b >= 0 ? (a >= 0 ? a : -a) : (a >= 0 ? -a : a); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace MathNet.Numerics.RootFinding |
||||
|
{ |
||||
|
public class RootFindingException : Exception |
||||
|
{ |
||||
|
public RootFindingException(string message, int iteration, double rangeMin, double rangeMax, double accuracy) |
||||
|
: base(message) |
||||
|
{ |
||||
|
Iteration = iteration; |
||||
|
RangeMin = rangeMin; |
||||
|
RangeMax = rangeMax; |
||||
|
Accuracy = accuracy; |
||||
|
} |
||||
|
|
||||
|
public int Iteration { get; set; } |
||||
|
public double RangeMin { get; set; } |
||||
|
public double RangeMax { get; set; } |
||||
|
public double Accuracy { set; get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using MathNet.Numerics.RootFinding; |
||||
|
using NUnit.Framework; |
||||
|
|
||||
|
namespace MathNet.Numerics.UnitTests.RootFindingTests |
||||
|
{ |
||||
|
[TestFixture] |
||||
|
public class BrentTest |
||||
|
{ |
||||
|
[Test] |
||||
|
public void MultipleRoots() |
||||
|
{ |
||||
|
double root = FindRoots.BrentMethod(x => x*x - 4, -5, 5, 1e-14, 100); |
||||
|
Assert.AreEqual(0, root*root - 4); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue