/// <summary> /// Returns the derivative of a certain degree for the polynom. /// If the current polynom is of degree N, its derivative of degree D /// is also a polynomial function of degree Max(0; N-D). /// </summary> /// <param name="derivativeDegree"></param> /// <returns></returns> public Polynom <T, C> Derivative(int derivativeDegree) { if (derivativeDegree == 0) { return(this); } else if (derivativeDegree < 0) { throw new ArgumentException("Derivative degree should be a non-negative integer value."); } else if (derivativeDegree > this.Degree) { return(new Polynom <T, C>(new Numeric <T, C>[] { Numeric <T, C> .Zero })); } int newCoefCount = this.coefficients.Length - derivativeDegree; Numeric <T, C>[] coefficients = new Numeric <T, C> [newCoefCount]; for (int i = 0; i < newCoefCount; i++) { coefficients[i] = this.coefficients[i + derivativeDegree]; for (int j = 0; j < derivativeDegree; j++) { coefficients[i] *= calc.fromInt(i + derivativeDegree - j); } } return(new Polynom <T, C>(coefficients)); }
/// <summary> /// If the current calculator for the <typeparamref name="T"/> type does not support conversions /// from the double type AND the <paramref name="num"/> does not exceed the range of long, /// this method will result in smaller number of exceptions, because /// if <paramref name="num"/> value contains only integral part, the 'fromInt(int)' calculator method /// will be preferred over the 'fromDouble(double)'. /// </summary> /// <typeparam name="T">The type of numbers for the calculator.</typeparam> /// <param name="calculator">The calling calculator object.</param> /// <param name="num">The number which is to be converted to the <typeparamref name="T"/> type.</param> /// <returns>The <typeparamref name="T"/> value returned by either fromInt (preferred) of fromDouble calculator method.</returns> public static T fromDoubleSafe <T>(this ICalc <T> calculator, double num) { if (Numeric <double, CalcDouble> .Calculator.fracPart(num) == 0 && num > long.MinValue && num < long.MaxValue) { return(calculator.fromInt((long)num)); } return(calculator.fromDouble(num)); }
/// <summary> /// Returns the rectangle approximation of the function integral. /// </summary> /// <typeparam name="T">The type of function argument/value.</typeparam> /// <typeparam name="C">The calculator for the argument type.</typeparam> /// <param name="obj">The calling function object.</param> /// <param name="interval">The interval to approximate the integral on.</param> /// <param name="pointCount">The overall point count used to calculate the integral.</param> /// <returns></returns> public static T IntegralRectangleApproximation <T, C>(this IFunction <T, T> obj, BoundedInterval <T, C> interval, int pointCount) where C : ICalc <T>, new() { ICalc <T> calc = Numeric <T, C> .Calculator; // Если интервал нулевой, то ваще забей. if (interval.IsZeroLength) { return(calc.zero); } // Если нет, то ваще не забей. Numeric <T, C> step = calc.div(interval.Length, calc.fromInt(pointCount)); Numeric <T, C> two = (Numeric <T, C>) 2; Numeric <T, C> left = interval.LeftBound + step / two; Numeric <T, C> right = interval.RightBound - step / two; // Будем хранить элементы по порядку, чтобы при суммировании не терять ерунды. SortedSet <Numeric <T, C> > sorted = new SortedSet <Numeric <T, C> >(Numeric <T, C> .NumericComparer); for (int i = 0; i < pointCount; i++) { T current = left + (right - left) * (Numeric <T, C>)i / (Numeric <T, C>)(pointCount - 1); sorted.Add(obj.Value(current)); } // Теперь будем суммировать по порядку, начиная с самых маленьких. Numeric <T, C> sum = calc.zero; foreach (Numeric <T, C> element in sorted) { sum += element; } return(sum * step); }
// -------------------------------------------- // --------- ПОИСК НУЛЕЙ НА ИНТЕРВАЛЕ --------- // -------------------------------------------- /// <summary> /// Searches for the function root (zero value) on the interval [a; b]. /// The interval is counted as with INCLUSIVE bounds! /// /// The conditions of method success: /// /// 1. The function f(x) is continuous on the interval [a; b]. /// 2. The value f(a) and f(b) are of different signs. /// /// These conditions guarantee the existence of the zero on the interval [a; b]. /// The method results in finding ONE of these zeroes. /// </summary> /// <typeparam name="T">The type of function argument/value.</typeparam> /// <typeparam name="C">The calculator for the argument type.</typeparam> /// <param name="obj">The calling function object.</param> /// <param name="interval">The interval on which the zero is searched. The value </param> /// <param name="epsilonArg">The epsilon value of the argument interval. The 'root' argument would be actually any value in the interval [-epsilon; +epsilon]</param> /// <param name="epsilonFunc">The epsilon value of the function zero. That means, that any function value in the interval [-epsilonFunc; epsilonFunc] is considered zero value. This parameter is usually set to absolute zero (so that only true 0 counts), but may become useful when the function calculation method contains precision errors resulting in zero becoming 'non-zero'.</param> /// <returns>The argument value resulting in f(x) ~= 0.</returns> public static T ZeroSearchBisectionMethod <T, C>(this IFunction <T, T> obj, BoundedInterval <T, C> interval, Numeric <T, C> epsilonArg, Numeric <T, C> epsilonFunc) where C : ICalc <T>, new() { ICalc <T> calc = Numeric <T, C> .Calculator; Numeric <T, C> zero = Numeric <T, C> .Zero; Numeric <T, C> two = calc.fromInt(2); Numeric <T, C> left = interval.LeftBound; Numeric <T, C> right = interval.RightBound; Numeric <T, C> xMid; Func <T, T> abs = WhiteMath <T, C> .Abs; Func <T, int> sgn = WhiteMath <T, C> .Sign; Numeric <T, C> leftVal = obj.Value(left); Numeric <T, C> midVal; Numeric <T, C> rightVal = obj.Value(right); // -- флаг - чтобы не проделывать лишних вычислений. bool leftChanged = false; // ----- если на концах интервала ноль, то возвращаем сразу. if ((abs(leftVal) - zero) < epsilonFunc) { return(left); } else if ((abs(rightVal) - zero) < epsilonFunc) { return(right); } // --------- проверочка if (sgn(leftVal) == sgn(rightVal)) { throw new ArgumentException("Error: the function values are of the same sign on the interval bounds."); } // --------------------------------------------------------- while (true) { xMid = (right + left) / two; // ------- достигли требуемой точности - ура! if (xMid - left < epsilonArg) { return(xMid); } // ---------------------------------------------- if (leftChanged) { leftVal = obj.Value(left); } else { rightVal = obj.Value(right); } midVal = obj.Value(xMid); // ------------ ура! Нашли риальни ноль! -------- if (abs(midVal - zero) < epsilonFunc) { return(xMid); } else if (sgn(rightVal) * sgn(midVal) < 0) { leftChanged = true; left = xMid; } else if (sgn(leftVal) * sgn(midVal) < 0) { leftChanged = false; right = xMid; } else { throw new FunctionException(string.Format("Some particular method iteration failed, possibly due to the precision loss. The function is intended to be of different signs on the interval bounds, but the values are {0} and {1}.", leftVal, rightVal)); } } }