예제 #1
0
        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);
        }
예제 #2
0
        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));
        }
예제 #3
0
        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;
        }
예제 #4
0
        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;
        }
예제 #5
0
        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;
        }
예제 #6
0
        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);
        }
예제 #7
0
        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));
        }
예제 #8
0
        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));
            }
        }
예제 #9
0
        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));
            }
        }
예제 #10
0
 public static IEnumerable <Range <Frac> > ApproximateRoots(this Polynomial <XTerm> polynomial, Frac epsilon)
 {
     return(ApproximateRootsHelper(polynomial, epsilon).OrderBy(e => e.Min).Distinct());
 }
예제 #11
0
 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());
 }