2012-01-27

Halley Root Finder

Kolejna metoda znajdowania miejsc zerowych. W założeniach bardzo podobna do metody Newtona. Od funkcji wymaga się ciągłości pierwszej i drugiej pochodnej. Musimy znać wzory na te pochodne, by je ciągle przeliczać. Jest to metoda iteracyjna. Krok iteracji ma postać:

$x_1 = x_0 - \displaystyle \frac {2 f(x_0) f'(x_0)} {2 {[f'(x_0)]}^2 - f(x_0) f''(x_0)}$

Uzasadnienie jest dla mnie dość pokrętne. Weźmy funkcję:

$g(x) = \displaystyle\frac {f(x)} {\sqrt{|f'(x)|}}$

Miejsca zerowe g(x) to miejsca zerowe f(x). Miejsca zerowe f(x) o ile nie są miejscami zerowymi $f'(x)$ są miejscami zerowymi g(x). Funkcja g(x) ma wartości nieoznaczone w ekstremach. Funkcja ta została zbudowana w taki sposób nieprzypadkowo. Jeśli podstawimy ją do wzoru na metodę Newtona to otrzymamy:

$x_1=x_0-\displaystyle\frac{g(x_0)}{g'(x_0)}=\frac{f(x_0)}{\sqrt{\left|f'(x_0)\right|}}\left(\frac{\sqrt{\left|f'(x_0)\right|}}{f(x_0)}\right)'$
$x_1=\displaystyle\frac{f(x_0)}{\sqrt{\left|f'(x_0)\right|}} \frac{2 f'(x) \sqrt{|f'(x)|}} {2 {[f'(x)]}^2 - f(x) f''(x)}$

Podobnie jak w metodzie Newtona staramy się poszukiwania tą metodą przeprowadzić dwa razy, za każdym razem za punkt startowy obierając inny koniec zakresu (a,b). I podobnie jak w metodzie Newtona nie ma gwarancji na znalezienie miejsca zerowego.

public class CentralDifferenceMethod
{
    private double m_h;
    private Func<double, double> m_func;

    public CentralDifferenceMethod()
        : base()
    {
    }
        
    public CentralDifferenceMethod(Func<double, double> a_func, 
        double a_h = Constants.DOUBLE_PRECISION * 2)
    {
        m_h = a_h;
        m_func = a_func;
    }

    public double Derive1(double a_x)
    {
        return (m_func(a_x + m_h) - m_func(a_x - m_h)) / (2 * m_h);
    }

    public double Derive2(double a_x)
    {
        return (m_func(a_x + m_h) - 2 * m_func(a_x) + 
            m_func(a_x - m_h)) / (m_h * m_h);
    }

    public static  Func<double, double> Derive1(
        Func<double, double> a_func, double a_h)
    {
        CentralDifferenceMethod cdm = 
            new CentralDifferenceMethod(a_func, a_h);

        return (x) =>
        {
            return cdm.Derive1(x);
        };
    }

    public static Func<double, double> Derive2(
        Func<double, double> a_func, double a_h)
    {
        CentralDifferenceMethod cdm = 
            new CentralDifferenceMethod(a_func, a_h);

        return (x) =>
        {
            return cdm.Derive2(x);
        };
    }

    public static Func<double, double> Derive2(
        Func<double, double> a_func, 
        Func<double, double> a_d1, double a_h)
    {
        CentralDifferenceMethod cdm = 
            new CentralDifferenceMethod(a_d1, a_h);

        return (x) =>
        {
            return cdm.Derive1(x);
        };
    }
}

public abstract class RootFinder
{
    public int MaxIterations;
    public double AbsoluteError;

    public RootFinder()
    {
        MaxIterations = Constants.ROOT_FINDER_MAXIMUM_ITERATIONS;
        AbsoluteError = Constants.ROOT_FINDER_ABSOLUTE_ERROR;
    }

    public static RootFinder Create(RootFindingMethod a_method)
    {
        switch (a_method)
        {
            case RootFindingMethod.Bisection: 
                return new BisectionRootFinder();
            case RootFindingMethod.ModifedRegulaFalsi: 
                return new ModifiedRegulaFalsiRootFinder();
            case RootFindingMethod.RegulaFalsi: 
                return new RegulaFalsiRootFinder();
            case RootFindingMethod.Secant: 
                return new SecantRootFinder();
            case RootFindingMethod.Newton: 
                return new NewtonRootFinder();
            case RootFindingMethod.Halley: 
                return new HalleyRootFinder();
            default: throw new NotImplementedException();
        }
    }

    public abstract double FindRoot(double a_a, double a_b, 
        Polynomial a_poly);
    public abstract double FindRoot(double a_a, double a_b, 
        Func<double, double> a_func);
    public abstract double FindRoot(double a_a, double a_b, 
        Func<double, double> a_func, Func<double, double> a_d1);
    public abstract double FindRoot(double a_a, double a_b, 
        Func<double, double> a_func, Func<double, double> a_d1, 
        Func<double, double> a_d2);
}

public abstract class DerivativeRootFinder : RootFinder
{
    protected struct RootState
    {
        public double Root;
        public bool Finded;

        public RootState(double a_x, bool a_finded = true)
        {
            Debug.Assert(a_x.IsNumber());

            Root = a_x;
            Finded = a_finded;
        }
    }

    protected double FindRootFromBothSide(double a_a, double a_b, 
        Func<double, double> a_func, Func<double, 
        RootState> a_root_finder)
    {
        Debug.Assert(a_a < a_b);
        Debug.Assert(a_a.IsNumber());
        Debug.Assert(a_b.IsNumber());

        double ya = a_func(a_a);
        double yb = a_func(a_b);

        if (Math.Abs(ya) < AbsoluteError)
            return a_a;
        if (Math.Abs(yb) < AbsoluteError)
            return a_b;

        if (Math.Sign(ya) * Math.Sign(yb) > 0)
            return Double.PositiveInfinity;

        RootState x1 = a_root_finder(a_a);
        if (x1.Finded && (x1.Root > a_a) && (x1.Root < a_b))
            return x1.Root;

        RootState x2 = a_root_finder(a_b);
        if (x2.Finded && (x2.Root > a_a) && (x2.Root < a_b))
            return x2.Root;

        return Double.PositiveInfinity;
    }
}

public class HalleyRootFinder : DerivativeRootFinder
{
    public override double FindRoot(double a_a, double a_b, 
        Func<double, double> a_func)
    {
        return FindRoot(a_a, a_b, a_func, 
            CentralDifferenceMethod.Derive1(a_func, AbsoluteError),
            CentralDifferenceMethod.Derive2(a_func, AbsoluteError));
    }

    public override double FindRoot(double a_a, double a_b, 
        Func<double, double> a_func, Func<double, double> a_d1)
    {
        return FindRoot(a_a, a_b, a_func, a_d1, 
            CentralDifferenceMethod.Derive2(a_func, a_d1, AbsoluteError));
    }

    public override double FindRoot(double a_a, double a_b, 
        Func<double, double> a_func, Func<double, double> a_d1, 
        Func<double, double> a_d2)
    {
        return FindRootFromBothSide(a_a, a_b, a_func, 
            (s) => FindRoot(s, a_func, a_d1, a_d2));
    }

    private RootState FindRoot(double a_x, Func<double, double> a_func,
        Func<double, double> a_d1, Func<double, double> a_d2)
    {
        double prev_y = a_func(a_x);

        for (int i = 0; i < MaxIterations; i++)
        {
            double dy = a_d1(a_x);
            double ddy = a_d2(a_x);

            double den = 2 * dy * dy - prev_y * ddy;
            if (den.IsAlmostEquals(0))
                return new RootState(a_x);

            a_x = a_x - 2 * prev_y * dy / den;

            double y = a_func(a_x);
            prev_y = y;

            if (Math.Abs(a_x) > AbsoluteError)
            {
                if (Math.Abs(y / a_x) < AbsoluteError)
                    return new RootState(a_x);
            }
            else if (Math.Abs(y) < AbsoluteError)
                return new RootState(a_x);
        }

        return new RootState(a_x, false);
    }

    public override double FindRoot(double a_a, double a_b, 
        Polynomial a_poly)
    {
        Polynomial d1 = a_poly.Differentiate();
        Polynomial d2 = d1.Differentiate();
        return FindRoot(a_a, a_b, (x) => a_poly.Evaluate(x),
            (x) => d1.Evaluate(x), (x) => d2.Evaluate(x));
    }
}

Brak komentarzy:

Prześlij komentarz