АЛЬТЕРНАТИВНЫЕ ВАРИАНТЫ ЗАДАНИЯ (для 2008

advertisement
Задачи из Java-фреймворков MIPT (Math, GUI)
для выполнения в рамках курса «Программирование вычислительных моделей»
в 2008-2009 уч.г.
Цели выполнения задач
1 Получить/закрепить навыки эффективного использования чужого программного кода
при написании программ в области вычислений или визуализации (эти навыки наиболее часто требуются при работе над дипломами по моделированию). В частности, закрепляется как минимум один из следующих навыков:
1.1 навык чтения/понимания чужого кода (основной навык современного программиста);
1.2 навыки ОО-проектирования (в результате разбора исходного кода ОО-фреймворков);
1.3 навык поиска в Интернет программного кода (куска исходного кода или готовой библиотеки, необязательно open source), подходящего для решения (сложной) задачи; а
также навык использования такого кода совместно с уже имеющимся кодом.
2 Научиться реализовывать сложные алгоритмы (расчетов и визуализации), часто встречающиеся в задачах моделирования, причем делать это так, чтобы сложность не привела к потере гибкости программы (возможности замены составных частей алгоритма,
его повторного использования в других задачах и т.п.).
Общие замечания
Описываемые ниже задачи могут быть трех типов:
1. Реализовать некоторый алгоритм или семейство алгоритмов.
2. Подключить некоторую (найденную в Интернет) программную библиотеку, в которой
реализован нужный алгоритм или семейство алгоритмов; при необходимости модифицировать результаты вызова библиотечных функций.
3. В задачах промежуточного типа (типа 3) нужно разобраться с имеющимся в Интернет
кодом алгоритмов, и «умно скопировать» этот код в Java-программу. В этом случае не
надо «с нуля» реализовывать алгоритм (как в случае 1), но также не надо адаптировать
неизменный готовый код алгоритма к своей программе (как в случае 2).
Независимо от типа задачи, требуется обеспечить достаточную степень гибкости
программной реализации (дальнейшее развитие и повторное использование алгоритма
или его частей).
Ради экономии времени все задачи построены так, чтобы студентам не приходилось
писать все части законченной программы ради проверки реализованных/подключенных
результатов (и даже не приходилось готовить для них исходные данные).
Ниже описание каждой задачи включается следующие разделы:
o Дано: описание того места в Java-фреймворке MIPT, которое подлежит изменению.
o Проблемы: почему это место следует изменять.
o Задача: что конкретно следует изменять, какие классы/методы фреймворка при этом
использовать. Здесь же указывается тип задачи (1 или 2, см. выше).
o ООП: дополнительные требования с точки зрения структуры программного кода (повышения степени reuse и других принципов OO-проектирования).
o Информация: примеры того, какими внешними описаниями и/или кодами можно воспользоваться для решения задачи. Если этот раздел опущен или недостаточен, перед поисками в
google (или даже вместо них) стоит посмотреть на ссылки отсюда, а также в книгу Numerical Recipes.
o Проверка: как протестировать код. В 2008-2009 уч.г. для тестирования алгоритмов используются,
в основном, «лабы» – программы лабораторного практикума МФТИ по вычислительной математике.
Алгоритмы научных расчетов (проект MIPT Math)
1. Алгоритмы линейной алгебры (операции над матрицей)
Дано: mipt.math.array.AbstractMatrix – частичная реализация интерфейса Matrix (где
определены операции над матрицами) и абстрактного класса Number (где определены, в
частности, арифметические операции). AbstractMatrix содержит реализацию всех часто
используемых операций над матрицей и вычислений ее свойств, но не фиксирует способ
хранения данных матрицы (который реализуется в подклассах). В частности, имеет реализацию интерфейсных методов Matrix inverseMatrix() (возвращает обратную матрицу) и
double modulusValue() (возвращает детерминант), int signum() (возвращает «знак»), а также неинтерфейсных (реже используемых) методов: det(), positiveDefiniteness(),
getEigenValues().
Проблемы: Все перечисленные методы сейчас реализованы через open-source библиотеку Jama, однако это
а) приводит к большим потерям производительности в силу необходимости конвертировать данные AbstractMatrix в Jama.Matrix (и обратно);
б) лишает AbstractMatrix возможности оперировать с числами произвольной природы, что
предполагается в концепции проекта MIPT Math. Согласно этой концепции числа (в данном случае – элементы матрицы) могут быть не только обычными вещественными числами (скалярными, двойной
точности; ScalarNumber) и комплексными числами (ComplexNumber), но и числами произвольной разрядности (с заданным числом десятичных разрядов; BigNumber), нечеткими числами (с той или иной формализацией понятия неточности исходных данных; FuzzyNumber) или даже векторами/матрицами (это иногда бывает удобно в сложных алгоритмах типа матричной прогонки).
Задача (тип 3 или 1): Реализовать указанные методы, используя арифметические
операции над числами (методы add, minus, mult, divide, setMinus, setInverse() класса Number), т.е. без преобразования произвольного числа к его скалярно-вещественному представлению двойной точности (doubleValue()).
ООП: Необходимо создать в AbstractMatrix поле и класс делегата, которому делегируются все вышеуказанные методы (такой класс, который можно назвать LinAlgebra и который должен иметь доступ к данным AbstractMatrix, например, через аргумент своих методов). Должно быть две реализации делегата: в одну из которых переносится существующий код, работающий через Jama, а во второй реализации находится новый код. ООпонятие делегата предполагает возможность его установить извне (сеттер) и шаблон «создания при необходимости» (причем Jama-делегата нужно создавать только в том случае,
если класс Jama.Matrix имеется в runtime classpath; сам AbstractMatrix должен успешно
компилироваться и без Jama*.jar в project classpath). За счет делегата решается еще одна проблема
класса AbstractMatrix – его избыточно большой размер и недостаточно высокая связность (low cohesion связано c объединением в одном классе двух сложных концепций – произвольной структуры матрицы и методов линейной алгебры). Оба делегата (JamaLinAlgebra и InternalLinAlgebra), а также их интерфейс или абстрактный класс LinAlgebra рекомендуется поместить в новый пакет mipt.math.array.impl.
Информация: например, можно сделать «умный copy-paste» исходного кода той же
библиотеки Jama и/или воспользоваться описаниями/кодами с algolist.
Проверка: лаба SLAE (генерирует матрицы, выдает их свойства, решает системы уравнений)
уже использует AbstractMatrix (и работает правильно), так что при проверке достаточно
сравнить работу лабы в старой и новой версии (с JamaLinAlgebra и с InternalLinAlgebra).
2. Численное дифференцирование
Дано: mipt.math.function.diff.DefaultDifferentiator – основываясь на заданном в качестве параметра порядке точности (от 1 до 4), он реализует 4 метода численного дифференцирования (причем методы для 1 и 3 порядков имеют 2 модификации – левая или правая разность).
Проблема: нет прикладных (более точных) методов.
Задача (тип 1 или 3): реализовать как минимум алгоритм Риддерса. Можно подключить внешнюю библиотеку, если такая найдется, хотя это будет вряд ли эффективно.
ООП: если делать только один метод (Риддерса), то нужно сделать делегата (аналогично идеям, описанным в пункте 1). Если более одного нового метода, то ради экономии
времени достаточно сделать подкласс или подклассы (хотя в этом случае делегат еще полезнее).
Информация: Numerical Recipes, раздел 5.7.
Проверка: Лаба NumDiff (правда, подключить к ней новый метод должен преподаватель, а не студент).
3. Сплайн-интерполяция
Данная задача отличается от остальных тем, что здесь меньше нужно разбираться с
чужим кодом и больше места для проектирования своих классов (дизайн не задан жестко),
а также тем, что для проверки необходимо решить часто встречающуюся на практике задачу: адаптацию интерфейса своих классов к интерфейсу конкретного приложения (лабы).
Дано: классы пакета mipt.math.function.interp – пример мини-фреймворка для интерполяции значений y по заданной (упорядоченной по x) таблице, а также вспомогательные
классы пакета mipt.math.array.search (необходимые для поиска того отрезка таблицы, по
которому производить интерполяцию).
Проблемы: 1) указанные пакеты работают с double, а не с Number (впрочем, это не
главная проблема); 2) пока в указанном пакете реализована только линейная интерполяция (в пакете mipt.crec.lab.compmath.interp.math.methods лабы Interp есть и другие методы,
но их интерфейс весьма сильно привязан к лабе; впрочем, даже там пока нет сплайнов).
Задача (тип 1 или 3): Кроме линейной интерполяции, в том же пакете
mipt.math.function.interp нужно реализовать работающий с Number’ами локальный сплайн
по трем точкам LocalSplineInterpolator (следует заметить, что сетка x’ов неравномерная,
поэтому поиск формулы для такого сплайна требует времени). При наличии времени,
нужно также реализовать нелокальный кубический сплайн GlobalSplineInterpolator (Шонберга; два варианта) – это один из самых полезных для практики методов интерполяции.
ООП: Проблему №1 нужно решить не путем copy-paste, а с повторным использованием кода за счет применения generic-типа в абстрактных классах типа
AbstractDoubleInterpolator (желательно избежать copy-paste полностью, но это необязательно, т.е., например, линейные интерполяторы для double и Number могут иметь логически эквивалентные куски–формулы).
Информация: а) Иванов В.Д. и др. Лабораторный практикум "Основы вычислительной математики", разделы 2.11, 2.12 – там описываются локальные и нелокальные
сплайны. б) Numerical Recipes, главы 3.3, 3.4 или любые другие источники. При необходимости доступен C++-код всех нужных сплайнов (из старой версии лабораторного практикума МФТИ).
Проверка: Лаба Interp содержит файл methods.xml, из которого с помощью reflection
загружаются разные методы. Туда необходимо дописать название своего класса, который
будет адаптером интерфейса mipt.crec.lab.compmath.interp.math.methods.AbstractMethod к
созданным алгоритмам LocalSplineInterpolator и CubicSplineInterpolator.
4. Вычисление элементарных функций
Дано: классы пакета mipt.math.function.impl.big – реализации элементарных функций
для mipt.math.BigNumber – чисел произвольной разрядности (precision); их следует использовать для сравнения. Интерфейс mipt.math.function.set.DependenceElements – создатель функций по имени (используемый при анализе формул, заданных в виде строки) и
его реализация mipt.math.function.set.BigDependenceElements, создающая Big-функции.
Проблема: сейчас реализации Big-функций сводятся к стандартным функциям java.lang.Math.*, что для precision>15 дает неверный результат.
Задача (тип 3 или 1): в пакете mipt.math.function.impl.any реализовать стандартные
функции «с нуля», используя при этом арифметические операции интерфейса
mipt.math.Number.
ООП: достаточно просто наследоваться от тех же классов, что и классы пакета
mipt.math.function.impl.big.
Информация: http://algolist.manual.ru/maths/count_fast/.
Проверка: Лаба NumDiff вычисляет значения как введенной функции, так и ее производной, причем она (как и остальные лабы) использует BigDependenceElements (если
установить «точность машинного числа = произвольная»). Важно проверить (при дифференцировании формулой 4го порядка с мелким шагом), что если «число десятичных знаков» меньше 16, результирующая погрешность совпадает с двойной точностью, а если
число больше – начинают отличаться в сторону улучшения точности.
Алгоритмы визуализации научных данных (проект MIPT GUI)
1. Расстановка чисел на оси графика
Дано: классы mipt.gui.graph.impl.*TickArrangement – реализации политики размещения на оси «тиков» (major ticks) – черточек, возле которых пишутся числовые значения.
Их абстрактный суперкласс mipt.gui.graph.TickArrangement хранит исходные данные –
минимальное и максимальное значения оси (вычисленные из данных графика) и заданное
в свойствах графика число интервалов между тиками N, а также результаты – значение
первого тика first и длина интервала d. Реализация может изменять значение N (с целью
обеспечить «наиболее красивые» числа возле тиков), однако изменять «не слишком сильно». Результирующая область не обязательно совпадает с заданной «минимальной» областью (first необязательно совпадает с заданным минимумом, а first+d*N – c заданным максимумом), однако она должна включать в себя заданную область.
Проблема: имеющаяся реализация DefaultTickArrangement (пытающаяся по максимально приблизить результирующую область к заданной) писалась разными программистами и без использования каких-либо справочных материалов, поэтому ее код не наглядный и не эффективный, а результаты не всегда предсказуемы.
Задача (тип 1): реализовать StandardTickArrangement – политику, более привычную
пользователям графических, математических, финансовых или иных подобных пакетов
(расширяющую результирующую область до «круглых» чисел). Функциональные требования к реализации следует выработать самому с использованием прототипов – либо коммерческих пакетов, либо программ, написанных на других языках.
ООП: не требуется (класс задан); требуется лишь процедурное программирование.
Информация: Интернет; пакеты Grapher, Mathcad, Matlab или подобные.
Проверка: Любая лаба содержит графики, однако наиболее сложные данные с точки
зрения данной задачи возникают в лабе SLAE (при варьировании разных методов). Для
подключения достаточно заменить DefaultTickArrangement на StandardTickArrangement в
методе Graph.createTickArrangement().
2. Сглаживание кривой графика квадратичными и кубическими сплайнами
Дано: классы mipt.gui.graph.function.*CurveRenderer – абстрактные и конечные реализации хранения точек кривой графика и отрисовки этой кривой. Абстрактная реализация InterpCurveRenderer использует Interpolator только для хранения точек, а для самой
интерполяции он используется только в одном подклассе – PseudoLinearCurveRenderer.
Проблема: отсутствуют «гладкие» отрисовщики (соединяющие точки более гладко,
чем отрезками прямых).
Задача (тип 1 или 1-2): с помощью Java2D реализовать отрисовщики, соединяющие
точки кривой отрезками а) парабол б) кубических парабол, причем так, чтобы соответствующая производная не имела разрыва в точках соединения (В Java2D такие отрезки
задаются своими tangent lines, поэтому задача сводится к определению параметров tangent
line, согласованных между двумя соседними отрезками). Можно подключить внешнюю
библиотеку, имеющую как минимум два этих вида сплайнов.
ООП: Желательно унаследоваться от InterpCurveRenderer, чтобы воспользоваться
интерполятором (квадратичную и кубическую реализации которого нужно создать самому; в функции интерполятора в данном случае входит не выдача значения y для заданного
x, как в PseudoLinearCurveRenderer, а выдача параметров). Как альтернатива, можно
наследоваться от AbstractCurveRenderer и не использовать никаких других объектов (тогда
необходимо самостоятельно хранить точки кривой).
Информация: Java2D manual, Интернет.
Проверка: Лаба Integr при установленном методе Симпсона сейчас отрисовывает
интегрируемую кривую PseudoLinearCurveRenderer; для тестирования достаточно заменить его на свой CurveRenderer и использовать небольшое число точек.
3. Визуализация скалярного поля изолиниями в 2D
Путем вызова библиотеки из Интернет (тип 2).
Слишком сложно? Может, вместо этого – оптимизированный вариант заливки цветами? (на Java2D, но работающий – в качестве замены неработающему
mipt.gui.graph.field.PaintFillScalarFieldRenderer).
Download