public static Q Brent(FunctionOfOneVariable f, Q left, Q right) { // extra info that callers may not always want int iterationsUsed; Q errorEstimate; return Brent(f, left, right, new Q(1, 100000000), Q.Zero, out iterationsUsed, out errorEstimate); }
private static IEnumerable <Range <Frac> > ApproximateRootsHelper(this Polynomial <XTerm> polynomial, Frac epsilon) { if (epsilon <= 0) { throw new ArgumentOutOfRangeException("epsilon"); } if (!polynomial.Coefficients.Any()) { throw new InvalidOperationException("Everything is a root..."); } var degree = polynomial.Degree(); if (degree == 0) { return(new Range <Frac> [0]); } if (degree == 1) { return new Range <Frac>[] { -polynomial.Coefficient(XToThe(0)) / polynomial.Coefficient(XToThe(1)) } } ; var criticalRegions = polynomial .Derivative() .ApproximateRoots(epsilon) .ToArray(); if (criticalRegions.Length == 0) { criticalRegions = new Range <Frac>[] { Frac.Zero } } ; var lowerRoot = polynomial.BisectLower(criticalRegions.First().Min, epsilon); var upperRoot = polynomial.BisectUpper(criticalRegions.Last().Max, epsilon); var r0 = new[] { lowerRoot, upperRoot }.WhereHasValue(); var r1 = criticalRegions.Where(r => Frac.Abs(polynomial.EvaluateAt((r.Min + r.Max) / 2)) < epsilon); var interRegions = criticalRegions .Window(2) .Where(e => e.Count() == 2) .Select(e => new Range <Frac>(e.First().Max, e.Last().Min, false, false)) .ToArray(); var r2 = interRegions.Select(e => polynomial.Bisect(e.Min, e.Max, epsilon)).WhereHasValue(); return(r0.Concat(r1).Concat(r2)); }
public static Q Bisect(FunctionOfOneVariable f, Q left, Q right, Q tolerance, Q target, out int iterationsUsed, out Q errorEstimate) { if (tolerance <= Q.Zero) { string msg = string.Format("Tolerance must be positive. Recieved {0}.", tolerance); throw new ArgumentOutOfRangeException(msg); } iterationsUsed = 0; errorEstimate = tolerance * 2; // Standardize the problem. To solve f(x) = target, // solve g(x) = 0 where g(x) = f(x) - target. FunctionOfOneVariable g = delegate (Q x) { return f(x) - target; }; Q g_left = g(left); // evaluation of f at left end of interval Q g_right = g(right); Q mid; Q g_mid; if (g_left * g_right >= Q.Zero) { string str = "Invalid starting bracket. Function must be above target on one end and below target on other end."; string msg = string.Format("{0} Target: {1}. f(left) = {2}. f(right) = {3}", str, g_left + target, g_right + target); throw new ArgumentException(msg); } Q intervalWidth = right - left; for (iterationsUsed = 0; iterationsUsed < maxIterations && intervalWidth > tolerance; iterationsUsed++) { intervalWidth *= new Q(1, 2); mid = left + intervalWidth; if ((g_mid = g(mid)) == Q.Zero) { errorEstimate = Q.Zero; return mid; } if (g_left * g_mid < Q.Zero) // g changes sign in (left, mid) g_right = g(right = mid); else // g changes sign in (mid, right) g_left = g(left = mid); } errorEstimate = right - left; return left; }
public static Q Brent(FunctionOfOneVariable g, Q left, Q right, Q tolerance, Q target, out int iterationsUsed, out Q errorEstimate) { if (tolerance <= Q.Zero) { string msg = string.Format("Tolerance must be positive. Recieved {0}.", tolerance); throw new ArgumentOutOfRangeException(msg); } errorEstimate = tolerance * 2; // Standardize the problem. To solve g(x) = target, // solve f(x) = 0 where f(x) = g(x) - target. FunctionOfOneVariable f = delegate (Q x) { return g(x) - target; }; // Implementation and notation based on Chapter 4 in // "Algorithms for Minimization without Derivatives" // by Richard Brent. Q c, d, e, fa, fb, fc, tol, m, p, q, r, s; // set up aliases to match Brent's notation Q a = left; Q b = right; Q t = tolerance; iterationsUsed = 0; fa = f(a); fb = f(b); if (fa * fb > Q.Zero) { string str = "Invalid starting bracket. Function must be above target on one end and below target on other end."; string msg = string.Format("{0} Target: {1}. f(left) = {2}. f(right) = {3}", str, target, fa + target, fb + target); throw new ArgumentException(msg); } label_int: c = a; fc = fa; d = e = b - a; label_ext: if (Q.Abs(fc) < Q.Abs(fb)) { a = b; b = c; c = a; fa = fb; fb = fc; fc = fa; } iterationsUsed++; tol = 2.0 * t * Q.Abs(b) + t; errorEstimate = m = 0.5 * (c - b); if (Q.Abs(m) > tol && fb != Q.Zero) // exact comparison with 0 is OK here { // See if bisection is forced if (Q.Abs(e) < tol || Q.Abs(fa) <= Q.Abs(fb)) { d = e = m; } else { s = fb / fa; if (a == c) { // linear interpolation p = 2.0 * m * s; q = 1.0 - s; } else { // Inverse quadratic interpolation q = fa / fc; r = fb / fc; p = s * (2.0 * m * q * (q - r) - (b - a) * (r - 1.0)); q = (q - 1.0) * (r - 1.0) * (s - 1.0); } if (p > 0.0) q = -q; else p = -p; s = e; e = d; if (new Q(2, 1) * p < new Q(3, 1) * m * q - Q.Abs(tol * q) && p < Q.Abs(new Q(1, 2) * s * q)) d = p / q; else d = e = m; } a = b; fa = fb; if (Q.Abs(d) > tol) b += d; else if (m > Q.Zero) b += tol; else b -= tol; if (iterationsUsed == maxIterations) return b; fb = f(b); if ((fb > Q.Zero && fc > Q.Zero) || (fb <= Q.Zero && fc <= Q.Zero)) goto label_int; else goto label_ext; } else return b; }
public static Q Newton(FunctionOfOneVariable f, FunctionOfOneVariable fprime, Q guess, Q tolerance, Q target, out int iterationsUsed, out Q errorEstimate) { if (tolerance <= Q.Zero) { string msg = string.Format("Tolerance must be positive. Recieved {0}.", tolerance); throw new ArgumentOutOfRangeException(msg); } iterationsUsed = 0; errorEstimate = tolerance * 2; // Standardize the problem. To solve f(x) = target, // solve g(x) = 0 where g(x) = f(x) - target. // Note that f(x) and g(x) have the same derivative. FunctionOfOneVariable g = delegate (Q x) { return f(x) - target; }; Q oldX, newX = guess; for (iterationsUsed = 0; iterationsUsed < maxIterations && errorEstimate > tolerance; iterationsUsed++) { oldX = newX; Q gx = g(oldX); Q gprimex = fprime(oldX); Q absgprimex = Q.Abs(gprimex); newX = oldX - gx / gprimex; errorEstimate = Q.Abs(newX - oldX); } return newX; }
public static Q Newton(FunctionOfOneVariable f, FunctionOfOneVariable fprime, Q guess) { // extra info that callers may not always want int iterationsUsed; Q errorEstimate; return Newton(f, fprime, guess, new Q(1, 100000000), Q.Zero, out iterationsUsed, out errorEstimate); }
private static May <Range <Frac> > Bisect(this Polynomial <XTerm> polynomial, Frac minX, Frac maxX, Frac epsilon) { var minS = polynomial.EvaluateAt(minX).Sign; var maxS = polynomial.EvaluateAt(maxX).Sign; if (minS == 0) { return((Range <Frac>)minX); } if (maxS == 0) { return((Range <Frac>)maxX); } if (minS == maxS) { return(May.NoValue); } while (maxX - minX > epsilon) { var x = (minX + maxX) / 2; var y = polynomial.EvaluateAt(x); if (y == 0) { return((Range <Frac>)x); } if (y.Sign == minS) { minX = x; } else { maxX = x; } } return(new Range <Frac>(minX, maxX, false, false)); }
private static May <Range <Frac> > BisectUpper(this Polynomial <XTerm> polynomial, Frac minX, Frac epsilon) { var increasingLimitSign = polynomial.Coefficients.MaxBy(e => e.Key.XPower).Value.Sign; var minS = polynomial.EvaluateAt(minX).Sign; if (minS == increasingLimitSign) { return(May.NoValue); } if (minS == 0) { return((Range <Frac>)minX); } var d = Frac.One; while (true) { d *= 2; var maxX = minX + d; if (polynomial.EvaluateAt(maxX).Sign != increasingLimitSign) { continue; } return(polynomial.Bisect(minX, maxX, epsilon)); } }
private static May <Range <Frac> > BisectLower(this Polynomial <XTerm> polynomial, Frac maxX, Frac epsilon) { var maxCoef = polynomial.Coefficients.MaxBy(e => e.Key.XPower); var decreasingLimitSign = maxCoef.Value.Sign * (maxCoef.Key.XPower % 2 == 0 ? +1 : -1); var maxS = polynomial.EvaluateAt(maxX).Sign; if (maxS == decreasingLimitSign) { return(May.NoValue); } if (maxS == 0) { return((Range <Frac>)maxX); } var d = Frac.One; while (true) { d *= 2; var minX = maxX - d; if (polynomial.EvaluateAt(minX).Sign != decreasingLimitSign) { continue; } return(polynomial.Bisect(minX, maxX, epsilon)); } }
public static IEnumerable <Range <Frac> > ApproximateRoots(this Polynomial <XTerm> polynomial, Frac epsilon) { return(ApproximateRootsHelper(polynomial, epsilon).OrderBy(e => e.Min).Distinct()); }
public static Frac EvaluateAt(this Polynomial <XYTerm> polynomial, Frac x, Frac y) { return(polynomial.Coefficients.Select(e => Frac.Pow(x, (int)e.Key.XPower) * Frac.Pow(y, (int)e.Key.YPower) * e.Value).Sum()); }