Приведем полный код проекта DLL, построенный на данный момент:
using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace MathTools{ /// <summary> /// Аналог класса Math библиотеки FCL /// </summary> public class MyMath { //Константы класса const double TWOPI = 2 * Math.PI; const double EPS = 1E-9; //Статические методы класса /// <summary> /// Sin(x) /// </summary> /// <param name="x"> /// угол в радианах - аргумент функции Sin /// </param> /// <returns> /// Возвращает значение функции Sin для заданного угла /// </returns> public static double Sin(double x) { //Оптимизация - приведение к интервалу x = x % TWOPI; //Init double a = x; double res = 0; int k = 0; //Основные вычисления while (Math.Abs(a) > EPS) { res += a; a *= -x * x / ((2 * k + 2) * (2 * k + 3)); k++; } return res; } }} Поставленная цель достигнута - построена DLL, содержащая класс, метод которого позволяет вычислять по заданному аргументу функцию . Метод построен в полном соответствии с описанным алгоритмом. При его построении использованы две важные оптимизации. Во-первых, применено рекуррентное соотношение, позволяющее существенно ускорить время и точность вычисления функции (попробуйте объяснить, почему улучшаются оба эти параметра). Во-вторых, аргумент приведен к сравнительно небольшому интервалу, что увеличивает скорость сходимости и гарантирует работоспособность метода для больших значений . Если не делать этой оптимизации, то для больших по модулю значений метод может давать некорректные результаты, - проверьте это предположение.
Итак, все хорошо? Не совсем. Оптимизацию можно продолжить, правда, не столь уже существенную. Сейчас для вычисления значения переменной a требуется выполнить одно деление, пять умножений, два сложения, взятие результата с обратным знаком. Попробуйте самостоятельно написать новую версию метода с улучшенными показателями, не глядя на код, который я сейчас приведу. Я добавил в класс новую версию метода, сохранив для новой версии имя метода - Sin. В классе остался и старый метод, но уже с именем SinOld. Две версии, давая один и тот же результат вычислений, позволят нам в дальнейшем провести некоторые полезные исследования.
Вот код метода с дополнительной оптимизацией:
public static double Sin(double x) { //Оптимизация - приведение к интервалу x = x % TWOPI; //Init double a = x; double res = 0; int k = 0; double x2 = x * x; //Основные вычисления while (Math.Abs(a) > EPS) { res += a; k+=2; a *= -x2 / (k * (k + 1)); } return res; } Код метода стал элегантнее и короче: вместо пяти умножений теперь делается только два, и вместо двух сложений - одно.
Всегда ли нужно стараться написать оптимальный код? Знание оптимальных алгоритмов и написание оптимального кода говорит о профессионализме разработчика. В реальных проектах есть критические по времени (или по памяти) секции проекта, где оптимизация жизненно необходима. В некритических секциях часто важнее простота и понятность кода, чем его оптимизация. В нашем примере речь идет об алгоритме массового применения, а в таких случаях оптимизация необходима.
А теперь вернемся к технической стороне дела. Построим Решение, содержащее проект, для чего в Главном меню среды выберем пункт Build|Build Solution. В результате успешной компиляции будет построен файл с уточнением dll. Поскольку построенная сборка не содержит выполняемого файла, то непосредственно запустить наш проект на выполнение не удастся. Построим консольный проект, к которому присоединим нашу DLL, и протестируем, насколько корректно работают созданные нами методы. Заодно разберемся с тем, как строится консольный проект и как к нему подсоединяется сборка, содержащая DLL.