Санкт-Петербургский государственный технический университет

Физико-механический факультет
Кафедра прикладной математики
Диссертация допущена к защите
Зав. кафедрой
_________________В.Е.Клавдиев
"___"_________________2005 г.

выпускная работа бакалавра
Тема:"Использование кратномасштабного анализа для
редактирования кривых."

Направление: 510200 – Прикладная математика и информатика

Выполнил студент гр.5057/2

С.А.Шатских

Руководитель, к.ф.-м.н., доцент

Ю.П.Ильин

Санкт-Петербург
2005

Содержание
Введение…. ………………………………………………………………………3
1. Обзор литературы ……………………………………………………………4
2. Постановка задачи ……………………………………………………….…..5
3. Описание математической модели………………………………………….5
4. Описание алгоритмов………………………………………………………...7
4.1. Описание алгоритма получения матрицы P…………………………..7
4.1.1 Алгоритм Осло для вставки нескольких узловых значений………8
4.2. Описание алгоритма получения матрицы M…………………………8
4.3. Описание алгоритма получения матрицы Q………………………….9
5. Описание программы………………………………………………………..9
6. Пример работы программы при редактировании буквы "А"………...10
Заключение………………………………………………………………………17
Литература……………………………………………………………………….18
Приложение……………………………………………………………………...19

2

Введение.
Кривые играют основополагающую роль во многих приложениях графики. В
автоматизированном проектировании кривые, являющиеся контурами поперечного
сечения, часто используются для описания поверхностей. При анимации по ключевым
кадрам кривые используются для контроля параметрической интерполяции. Для
трехмерного моделирования и анимации используются

базовые кривые, которые

описывают деформацию объектов. При работе с графикой кривые применяются для
описания областей с постоянной текстурой или цветом, при разработке шрифтов кривые
представляют собой контуры букв, а для иллюстраций, выполненных от руки, кривые
являются основными элементами законченного фрагмента. Все эти приложения
выигрывают от такого представления кривых, которое делает возможным гибкое
редактирование, сглаживание и приближение к исходной кривой в пределах задаваемого
допуска.
Единообразный подход к рассмотрению всех вышеперечисленных проблем
обеспечивается кратномасштабным представлением кривой.
Кратномасштабное представление кривой не требует дополнительной памяти для
хранения, если не считать n точек исходного B-сплайна, а алгоритм, использующий его,
отличается простотой и быстрым выполнением, как правило, являясь линейным по n.
Полезность кратномасштабного представления кривых заключается в удобстве, с
которым может выполняться широкое множество различных операций. Ряд других
представлений кривых был изобретен для работы с одной или несколькими операциями из
этого множества.

3

1. Обзор литературы.
Данный аппарат можно применить и для работы с поверхностями. Ранее для
работы с поверхностями использовались другие механизмы. Форсей и Бартелс [1]
используют иерархические B-сплайны при редактировании общей формы поверхности с
сохранением ее деталей. Их первоначальная формулировка требует от пользователя
разработки явной, встроенной в модель, иерархии. В более поздней работе [2] они
описывают

метод,

позволяющий

осуществить

рекурсивную

аппроксимацию

иерархической поверхности до получения набора данных, для чего первым шагом
находится грубое приближение, а затем производится уточнение в тех областях, где
требуется большая точность приближения. Подобная схема напоминает действие блока
фильтров в кратномасштабном анализе, о котором будет сообщено позже. Фаулер [3], а
также Уэлш и Уиткин [4] описывают методы, позволяющие редактировать более узкие или
более широкие участки поверхности. Однако ни в одной из этих работ не предпринята
попытка сохранить высокочастотные детали при редактировании на более низком уровне
точности.

4

2. Постановка задачи.
Основной

целью

поставленной

задачи

являлось,

создание

программы,

предоставляющей новый инструмент для работы с кривыми, который отсутствует в
известных программах.

Программа должна

выполнять операции по редактированию

кривых на текущем масштабе и переходить от одного масштаба кривой к другому.

3. Описание математической модели.
Пусть нам дана физическая кривая. Заменяем ее математической моделью,
например, B-сплайном, который будет интерполировать или аппроксимировать кривую.
Параметры сплайна: узловой вектор X, порядок сплайна и определяющий многоугольник
n

{Bi} i =1 . Предполагается, что на вход подается кривая, заданная определяющим
n

многоугольником {Bi} i =1 , узловой вектор X такой, как для B-сплайнов, интерполирующих
концевые точки, порядок сплайна = размер узлового вектора – количество опорных точек
n

{Bi} i =1 .
Предлагаемое представление кривой должно допускать редактирование кривой и
возможность перехода от одного масштаба кривой к другому.
Для редактирования кривой берется представление кривой в виде B-сплайна, а
n

редактированию подвергаются коэффициенты {Bi} i =1 . Для этого используется средства
диалогового интерфейса программы .
n

Чтобы перейти от одного масштаба к другому нужно из набора точек {Bi} i =1
k

получить набор точек {Ci} i =1 (k < n) - представление кривой на другом уровне
l

масштабирования, а также набор точек {Di} i =1 - детализирующие коэффициенты. Затем
k

надо предоставить возможность пользователю отредактировать кривую, заданную {Ci} i =1 .
l

Возможно редактирование детализирующих коэффициентов {Di} i =1 , но в данной версии
алгоритма пользователю не предоставляется такой возможности. После этого надо
n

вернуться на прежний уровень масштабирования. При этом изменится {Bi} i =1 .
Процесс декомпозиции дискретной последовательности значений в средние и
детализирующие значения при различных масштабах называется кратномасштабным
анализом (multiresolution analysis). О нем в доступной форме можно прочитать в [6] и
в [5].

5

n

{Bi} i =1 задают кривую (функцию). Функцию можно разложить в заданном базисе,
т.е. представить как линейную комбинацию базисных функций. Существует система
вложенных подпространств V1 ⊂ V2 ⊂…⊂ Vj ⊂… . Wj-1 – ортогональное дополнение
пространства Vj-1 до Vj. Пусть υ(j) – размерность пространства Vj, ω(j) – размерность
пространства Wj. В каждом подпространстве Vj существует множество базисных функций
Φj (x) = [φj0(x) …φjυ(j)-1(x)]. Φj-1 (x) из Vj-1 раскладываются по Φj(x)⊂ Vj.

В каждом

подпространстве Wj существует множество базисных функций Ψj(x) = [ψj0(x) …ψjω(j)-1(x)].
Существует матрица Pj такая, что Φj-1 (x) = Φj(x)Pj.
Существует матрица Qj такая, что Ψj-1 (x) = Φj(x)Qj.
Рассмотрим функцию в некотором аппроксимационном пространстве Vj. Допустим, мы
имеем коэффициенты этой функции, выраженные в терминах некоторого базиса
масштабирующих функций. Мы можем представить эти коэффициенты в виде векторастолбца cj = [cj0

cjυ(j)-1]т. Предположим, что нам нужно создать версию cj с низким

разрешением, обозначим ее cj-1, характеризуемую меньшим количеством коэффициентов υ
(j-1). Стандартный метод получения υ(j-1) значений cj-1 задействует определенную форму
линейной фильтрации и децимацию υ(j) значений cj. Этот процесс можно выразить
матричным уравнением
cj-1 = Ajcj
где Aj является постоянной матрицей размерности υ(j-1)×υ(j). Поскольку cj-1 содержит
меньшее число коэффициентов по сравнению с cj, уже интуитивно можно понять, что в
результате процесса фильтрации произошла потеря доли информации. Мы можем собрать
эти потерянные детали в другой столбец dj-1, определяемый выражением
dj-1 = Bjcj
где Bj является постоянной матрицей размерности ω(j-1)×υ(j).
Процесс разбиения коэффициентов cj на более грубую версию cj-1 и уточняющие
коэффициенты dj-1 называется анализом или разложением. При надлежаще выбранных Aj и
Bj исходные коэффициенты можно восстановить из cj-1 и dj-1, воспользовавшись матрицами
Pj и Qj , полученными выше:
cj = Pjcj-1 + Qjdj-1
Получение cj из cj-1 и dj-1 называется синтезом, а Pj и Qj называют, в данном контексте,
фильтрами синтеза. Подробности можно посмотреть в [5] и [7].
Проблема с выбором вейвлетов.

6

Допустим, вы прочитали о вейвлетах Хаара и принципах конструирования
ортогональных вейвлетов, например в [5],[6] или [7] . Там говорится, что не нужно строить
матрицу P, а достаточно найти ядро свертки, тогда строки P будут иметь структуру
сдвинутых копий ядра свертки. Q будет иметь такую же структуру, но ядро свертки будет
другим. Однако тут возникает проблема: cj = [cj0

cjυ(j)-1]т - не бесконечный вектор. С

концами вектора обычную свертку провести не получится. Подробнее об этой проблеме
см. в [9]. Кроме того, в компьютерной графике используются сплайновые кривые, а
базисными функциями являются полиномы, а не периодические функции.
Поэтому предлагается использовать полуортогональные вейвлеты, построенные на
основе неравномерных B-сплайнов, интерполирующих концевые точки ([13],[5]). В [8]
упоминались B-сплайновые вейвлеты, но они были ортогональными. А в [12] проблему с
крайними (концевыми точками) предлагали решить так: работать только с замкнутыми
кривыми или применять редактирование, а, следовательно, и фильтр к части кривой, а
когда собираемся применить вейвлет преобразование, то удлиняем сегмент кривой за счет
остальной кривой, причем настолько, чтобы можно было применить фильтр к крайним
точкам.
В [5] масштабирующие функции Φj (x) получаются из Φj-1(x) путем вставки узлов.
Таким образом, в [5] получают матрицу Pj. Однако они делают это не рационально. Если n
– ширина матрицы P, то сложность их алгоритма O(n3). Они вставляют n узлов, при этом
каждый раз пересчитывая матрицу P. Для вставки узлов есть алгоритм Боэма [11] и
алгоритм Осло (см. в [10]). Можно понизить сложность алгоритма формирования матрицы
P, если воспользоваться алгоритмом Осло. Тогда сложность алгоритма получения P будет
O(n2). Кроме того, получаются рекуррентные формулы для элементов Pi,j матрицы P (см.
4.1.1).
Далее, матрица Q определяется как нуль пространство матрицы M = (Pj)т[(Φj|Φj)], где [(Φj|
Φj)] – тензорное произведение Φj на Φj. Если n – ширина матрицы Q, то сложность их
алгоритма получения матрицы M будет O(n4), однако, как показано в данной работе,
сложность можно понизить до O(n2).

4. Описание алгоритмов.
4.1. Описание алгоритма получения матрицы P.
Pj – это матрица перехода от базиса Φj(x) к базису Φj-1 (x).
Φj(x) – базис пространства Vj, состоящий из 2j+m неравномерных B-сплайнов (m – степень
базисной функции), сконструированных на последовательности узлов:

7

Чтобы сформулировать выражения для элементов матрицы уточнения Pj, можно
прибегнуть к теории введения узлов.
4.1.1. Алгоритм Осло для вставки нескольких узловых значений ([10]).
n

Рассмотрим исходную кривую F(t) = ∑ i =0 BiNi,k(t) с узловым вектором X = [x0 x1 … xn+k],
k=m+1 – порядок сплайна. Вставив узел, получаем новую кривую R(s):
l

R(s) =

CjMj,k(s)

j= 0

с новым узловым вектором Y = [y0 y1 … yl+k], где l>n. Надо найти новые вершины
определяющего многоугольника Cj , такие, что F(t)=R(s). По алгоритму Осло
n

Cj = ∑ α i, j Bi
k

,

j=0÷l

i= 0

k

где α i, j задаются рекурсивным соотношением:
1

α i, j =

1 если xi ≤ yj ≤ xi+1
0 в противном случае
k−1

k

k−1

α i, j = α i , j (yj+k-1 - xi)/(xi+k-1 - xi) + α i +1, j (xi+k - yj+k-1)/(xi+k - xi+1)
Таким образом, P определяется как матрица перехода от {Bi} к {Cj}.
k

α i, j - есть коэффициенты элементов Pi,j матрицы P.
4.2. Описание алгоритма получения матрицы M.
Матрицу M можно записать в виде M = [(Φj|Φj+1)] (см. [13]). Φj - базис пространства Vj, Φ
j+1

- базис пространства Vj+1.

Φj = [N11,k(t) … N1υ(j),k(t)]
Φj+1 = [N21,k(t) … N2υ(j+1),k(t)]
k – порядок сплайна, k = m+1.
1

Mi,j =

N1i,k(t)N2j,k(t)dt

0

N1i,k(t) определен на узловом векторе X из 2j+2*m+1 узлов, а N2j,k(t) – на узловом векторе X
из 2j+1+2*m+1 узлов.
Так как Ni,k(t) – сплайн степени m, то для вычисления интеграла нужно использовать
квадратурную формулу, точную для полиномов степени 2*m. Эту квадратурную формулу
нужно построить заранее. Размер матрицы M есть O(n2), сложность вычисления Mi,j O(m2), но не как не O(n2), как в [5]. Кроме того, если Mi,j нулевой, то я не трачу O(m2)
8

операций на вычисление интеграла. Количество ненулевых элементов Mi,j есть O(n). Тогда
cложность алгоритма получения матрицы M будет O(n2)+O(n)O(m2), а так как m2 – мало, то
сложность вычисления M можно считать равным O(n2).
4.3. Описание алгоритма получения матрицы Q.
Матрица Q определяется как нуль пространство матрицы M, т.е. как фундаментальная
система решений СЛОАУ(системы линейных однородных алгебраических уравнений)
MjQj = 0.

(*)

Существует не единственный базис для нуль-пространства прямоугольной матрицы
вроде Mj, из чего следует, что существует много различных базисов вейвлетов для данного
пространства вейвлетов Wj. Допустим, что мы уже знаем Mj и теперь хотим выбрать Qj,
чтобы определить вейвлеты. Нам необходимо наложить дополнительные ограничения в
дополнение к требованию ортогональности, обозначенному в уравнении (*). Если мы
хотим, чтобы наши вейвлеты имели компактные носители, то следует сделать количество
ненулевых последовательных элементов в столбцах матрицы Qj по возможности
минимальным. Значения этих элементов можно найти, рассматривая отдельно каждый
столбец и решая систему линейных ограничений из уравнения (*).

5. Описание программы.
Программа создавалась в интегрированной среде разработки Builder 6.0. Программа
позволяет создавать B-сплайны, интерполирующие концевые точки. Кроме того, имеется
возможность редактировать точки и переходить к соседнему масштабу кривой. Также
предусмотрена возможность для записи параметров кривой в файл и, соответственно,
чтение их из файла.
Описанный выше алгоритм кратномасштабного анализа работает не с любыми
сплайнами, а только с теми, у которых количество опорных точек n = 2j+m, m – степень
сплайна, j – произвольно. Что делать, если исходная кривая задается количеством точек, не
удовлетворяющих указанному соотношению? Для этого исходная кривая заменяется
похожей кривой, допускающей кратномасштабный анализ. В таких случаях в
разработанной программе берется крайняя точка Bn и делается кратной. Кратность точки
z

Bn такова, чтобы в новом определяющем многоугольнике {Bi} i =1 количество точек z стало
2j+m.
Заменять исходную кривую на приближенную можно и по другому, например,
используя метод наименьших квадратов. Кроме того, исходную кривую можно заменить
двумя или более кривыми, допускающими кратномасштабный анализ, но тогда будет
проблема соединения концов.
9

Описание модулей.
Модули FindP и FindQ формируют матрицы синтеза P и Q соответственно. Модуль
synthesis из низкочастотных и уточняющих коэффициентов cj-1 и dj-1 уровня j-1 получает
низкочастотные

коэффициенты cj уровня j. Модуль analysis из низкочастотных

коэффициентов cj уровня j получает низкочастотные и уточняющие коэффициенты cj-1 и dj1

уровня j-1. Модуль Project отвечает за интерфейс с пользователем и связывает эти и

вспомогательные модули.
Исходные тексты модулей FindP и FindQ представлены в Приложении.

6. Пример работы программы при редактировании буквы "А".
На рисунке 1 изображена кривая в виде буквы "А", которая создана с помощью редактора
моей программы. Уровень масштаба j=3. Количество точек n=11.
На рисунке 2 представлена кривая в виде буквы "А", но с меньшим масштабом. Уровень
масштаба j=2. Количество точек n=7.
На рисунке 3 представлена кривая в виде буквы "А", но с меньшим масштабом. Уровень
масштаба j=1. Количество точек n=5. На этом уровне производится редактирование
кривой. При этом меняется местоположение одной точки, т.е меняется координата Y на
130 пунктов (пикселей).
На рисунке 4 показан результат данного сдвига точки. Масштаб прежний. Уровень
масштаба j=1. Количество точек n=5.
На рисунке 5 показана отредактированная кривая с масштабом j=2 и количеством точек
n=7, которая получена путем восстановления (синтеза) из уровня j=1.
На рисунке 6 показана отредактированная кривая с масштабом j=3 и количеством точек
n=11, которая получена путем восстановления (синтеза) из уровня j=2.
Из примера видно, что передвинув одну точку на уровне j=1, мы изменили
положение 7 точек на уровне j=3.

10

Рис. 1. Исходная кривая. Уровень масштаба j=3. Количество точек n=11.

11

Рис. 2. Переход с соседнему уровню. Уровень масштаба j=2. Количество точек n=7.

12

Рис. 3. Уровень масштаба j=1. Количество точек n=5. На этом уровне производится
редактирование кривой. При этом меняется местоположение одной точки, т.е меняется
координата Y на 130 пунктов (пикселей).

13

Рис. 4. На рисунке 4 показан результат данного сдвига точки. Уровень масштаба j=1.
Количество точек n=5.

14

Рис. 5. На рисунке 5 показана отредактированная кривая с масштабом j=2 и количеством
точек n=7, которая получена путем восстановления (синтеза) из уровня j=1.

15

Рис. 6. На рисунке 6 показана отредактированная кривая с масштабом j=3 и количеством
точек n=11, которая получена путем восстановления (синтеза) из уровня j=2.

16

Заключение.
Основным

результатом данной бакалаврской работы является разработка

программы для создания и редактирования на различных уровнях масштаба B-сплайнов,
интерполирующих концевые точки. Для этого были произведены следующие этапы:
1. Исследован теоретический материал по представлению кривых.
2. Исследована методика конструирования полуортогональных вейвлетов.
3. Исследована и проанализирована методика формирования матриц синтеза
P и Q.
4. Разработаны алгоритмы управления модулями программы.
5. Разработан интерфейс приложения.
6. Программа протестирована на пробной задаче редактирования буквы "А".
Дополнительно для работы исходной программы были созданы подпрограммы для
получения матриц синтеза P и Q со сложностью, меньшей, чем у Э. Столница, Т. ДеРоуза
и Д. Салезина ([5]).
Кроме того, были получены рекуррентные формулы для элементов Pi,j матрицы P и
явные формулы для коэффициентов матрицы M, что можно использовать для дальнейшего
изучения этих матриц и, например, для определения нулевых элементов.
В результате созданная программа выполняет операции по редактированию кривых
на текущем масштабе, а также позволяет переходить от одного масштаба кривой к
другому, и является новым инструментом для работы с кривыми, который отсутствует в
известных программах.

17

Литература.
[1] D. Forsey and R. Bartels. Hierarchical B-spline refinement. Computer Graphics, 22(4):205212, 1988.
[2] D. Forsey and R. Bartels. Tensor products and hierarchical fitting. In Curves and Surfaces in
Computer Vision and Graphics II, SPIE Proceedings Vol. 1610, pages 88-96, 1991.
[3] B. Fowler. Geometric manipulation of tensor product surfaces. In Proceedings of the 1992
Symposium on Interactive 3D Graphics, March1992. Available as Computer Graphics, Vol. 26,
No. 2.
[4] W. Welch and A. Witkin. Variational surface modeling. Computer Graphics, 26(2):157-166,
1992.
[5] Э. Столниц, Т. ДеРоуз, Д. Салезин "Вейвлеты в компьютерной графике"
[6] С. Уэлстид "Фракталы и вейвлеты для сжатия изображений в действии"
[7] Левкович-Маслюк, Л. Переберин А. Введение в вейвлет-анализ. Учебный курс. Москва: Графикон 99
[8] В. Дьяконов "Вейвлеты от теории к практике"
[9] В. И. Воробьев, В. Г. Грибунин "Теория и практика вейвлет-преобразования"
[10] Д. Роджерс "Математические основы машинной графики"
[11] W. Boehm "Inserting new knots into B-spline curves" IPC Business Press, Vol. 12, No. 4,
july 1980, pages 199-201.
[12] Faramarz F. Samavati, Mai Ali Nur, Richard Bartels, and Brian Wyvill "Progressive Curve
Representation Based on Reverse Subdivision" Department of Computer Science University of
Calgary, Calgary, Canada
[13] Adam Finkelstein and David H. Salesin "Multiresolution Curves" Department of Computer
Science and Engineering University of Washington Seattle, Washington 98195 April 1994
Technical Report 94-01-06b

18

Приложение.
Модуль FindP.cpp
// данный модуль предназначен для нахождения матрицы синтеза P
#include<iostream.h>
#include<stdlib.h>
#include<conio.h>
#include<stdio.h>
#include"findp.h"
class matrix_alfa
{
public:
struct alfa_k *my_alfa;
double *X;
double *Y;
int t_j;
int m;
int k;
matrix_alfa(int t_j1,int m1,int k1,double *X1,double *Y1);
~matrix_alfa(void);
double alfa(int k,int i,int j);
};
struct alfa_k
{
double data;
int flag; // = = 1, если значение alfa_k было вычислено ранее
};
//-------------------------double *FindP(int m,int j)
{ // находим матрицу синтеза P
int i;
int t_j_1 = pow(2,j-1);
int t_j = pow(2,j);
int k = m+1; // порядок сплайна
double *X;
double *Y;
19

X = (double *)calloc(pow(2,j-1)+2*m+1,sizeof(double));
if(X==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
Y = (double *)calloc(pow(2,j)+2*m+1,sizeof(double));
if(Y==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
for(i=1;i<=k;i++)
{
X[i-1] = 0;
Y[i-1] = 0;
}
for(i=t_j+2*m+1;i>t_j+2*m+1 -k;i--)
{
Y[i-1] = 1;
}
for(i=t_j_1+2*m+1;i>t_j_1+2*m+1 -k;i--)
{
X[i-1] = 1;
}
for(i=1; i<=t_j-1 ;i++)
{
Y[i+k-1] = i/(double)t_j;
}
for(i=1; i<=t_j_1-1 ;i++)
{
X[i+k-1] = i/(double)t_j_1;
}
double *P;
P = (double *)calloc((t_j_1+m)*(t_j+m),sizeof(double));
20

if(P==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
matrix_alfa my_alfa(t_j,m,k,X,Y);
cout<<endl;
for(j=1;j<=t_j+m;j++)
{
for(i=1;i<=t_j_1+m;i++)
{
// P(i,j) = alfa(k,i,j)
P[(t_j_1+m)*(j-1)+(i-1)] = my_alfa.alfa(k,i,j);
}
}
free(Y);
free(X);
return P;
}
//----------------------double matrix_alfa::alfa(int k,int i,int j)
{// вычисляем значение alfa( k,i,j)
double alfa1,alfa2;
double d;
if(i==3 && j==3)
{
//

int q = (t_j_1+m)*(t_j+m)*(k-1) +(t_j_1+m)*(j-1)+(i-1);
cout<<"";

}
if(my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].flag==1)
{
return my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].data;
}
21

else
{
if(k==1)
{
if(X[i-1]<=Y[j-1] && Y[j-1]<X[i+1 -1])
{
my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].flag = 1;
my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].data = 1;
}
else
{
my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].flag = 1;
my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].data = 0;
}
}
else
{
alfa1 = alfa(k-1,i,j);
if(alfa1!=0)
{
d = X[i+k-1 -1] - X[i-1];
if(d==0)
{
cout<<"\nerror divisionning by zero";
exit(1);
}
alfa1 = alfa1*(Y[j+k-1 -1] - X[i-1])/d;
}
alfa2 = alfa(k-1,i+1,j);
if(alfa2!=0)
{
d = X[i+k -1] - X[i+1 -1];
if(d==0)
{
cout<<"\nerror divisionning by zero";
22

exit(1);
}
alfa2 = alfa2*(X[i+k -1] - Y[j+k-1-1])/d;
}
my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].flag = 1;
my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].data = alfa1 + alfa2;
}
return my_alfa[(t_j+m)*(t_j+m)*(k-1) +(t_j+m)*(j-1)+(i-1)].data;
}
}
//-------------------------matrix_alfa::matrix_alfa(int t_j1,int m1,int k1,double *X1,double *Y1)
{// создаем матрицу для alfa
t_j = t_j1 ;
m = m1;
k = k1;
X = X1;
Y = Y1;
my_alfa = (struct alfa_k *)calloc((t_j+m)*(t_j+m)*k,sizeof(struct alfa_k));
if(my_alfa==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
}
//----------------------matrix_alfa::~matrix_alfa(void)
{// деструктор
free(my_alfa);
}
//-------------------------int pow(int i,int j)
{
// возвращаем i^j
23

int p = 1;
int k;
for(k = 1; k<= j;k++)
{
p = p*i;
}
return p;
}
//--------------------

24

Модуль FindQ.cpp
// данный модуль предназначен для нахождения матрицы синтеза Q
#include<iostream.h>
#include<stdlib.h>
#include<conio.h>
#include<stdio.h>
#include<math.h>
#include "gauss.h"
#include "gauss_Jo.h"
#include"findq.h"
struct a_b
{
double a;
double b;
int flag; // == 0 если пустое пересечение
};
struct a_b intersection_length(double a,double b,double c,double d);
double* FindM(int m,int j);
//-------------------------double * FindQ(int m,int j)
{ // находим матрицу синтеза Q
int t_j_1 = pow(2,j-1);
int t_j = pow(2,j);
int n = t_j_1;
int m2 = t_j+m;
int m1 = t_j_1+m;
int i;
double *M;
double *Q;
double *A;
double *b;
// (A|(-b)) == submatrix
M = FindM(m,j);
25

Q = (double *)calloc(n*m2,sizeof(double));
if(Q==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
A = (double *)calloc(m1*m2,sizeof(double));
if(A==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
b = (double *)calloc(m2,sizeof(double));
if(b==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
//size(M) = (t_j_1+m)*(t_j+m)
int found = 0; // найденный столбец в матрице Q
int start_col = 0;
int width;
int start_col_1;// левый верхний ненулевой элемент
int end_col_2; // правый нижний ненулевой элемент
// матрицы submatrix
int rank_def;
int dim_q_col;
double *q_col;
double max;
start_col_1 = 0;
end_col_2 = 0;
while( (found < n/2) && (start_col < m2) )
{
26

start_col = start_col + 1 + ((found > m)? 1 : 0);
width = 0;
rank_def = 0;
// find submatrix
while(M[(t_j+m)*(start_col_1) + start_col-1]==0)
{
start_col_1++;
}
// start_col_1 теперь найден
end_col_2 = start_col_1;
while (!rank_def && (width < m2 - start_col + 1))
{
width = width + 1;
// find end_col_2
while((end_col_2+1)<m1 && M[(t_j+m)*(end_col_2+1) + start_col-1+width1]!=0)
{
end_col_2++;
}
//

end_col_2--;
if(width-(end_col_2-start_col_1 +1) > 0)
{
rank_def = 1;
}
}
// сейчас найдены start_col и width
if(rank_def)
{
dim_q_col = width;
for(i=0;i<dim_q_col-1;i++)
{
for(j=0;j<dim_q_col-1;j++)
{

27

A[i*(dim_q_col-1)+j] = M[(i+start_col_1)*(t_j+m)+(start_col-1
+j)];
}
}
for(i=0;i<dim_q_col-1;i++)
{
b[i] = -M[(i+start_col_1)*(t_j+m)+(start_col-1 + dim_q_col-1)];
//printf("\n%2.5lf ",b[i] );
}
//cout<<endl;
q_col = mygaus1(A,b,dim_q_col-1);
q_col[dim_q_col-1] = 1;
// q_col найден
// нормализуем q_col
max = fabs(q_col[0]);
for(i=1;i<dim_q_col;i++)
{
if(fabs(q_col[i]) > max)
{
max = fabs(q_col[i]);
}
}
//cout<<endl;
for(i=0;i<dim_q_col;i++)
{
q_col[i] = q_col[i]/max;
}
// change sign to give consistent orientation
// изменяем знак, чтобы давать последовательную ориентацию
if((start_col + int(floor(double(m+1)/2.0)) + ((q_col[0] > 0)? 1 : 0)) % 2 != 0)//
не четно
{
for(i=0;i<dim_q_col;i++)
{
q_col[i] = -q_col[i];
28

}
}
// put column into left half of Q
found = found + 1;
//Q(start_col: start_col + width - 1, found) = q_col;
for(i=start_col-1,j=0;i<start_col + width - 1;i++,j++)
{
Q[(n)*i + found-1] = q_col[j];
}
// use symmetry to put column into right half of Q
// in reverse order and negated if degree is even
// Q(:, n - found + 1) = flipud(Q(:, found))*(-1)^(d + 1);
if((m+1)%2 !=0)
{
for(i=0;i<dim_q_col;i++)
{
q_col[i] = -q_col[i];
}
}
for(i=start_col,j=0;i<=start_col + width - 1;i++,j++)
{
Q[n*(m2-i) + n-found+1-1] = q_col[j];
}
}
};
free(b);
free(A);
free(M);
return Q;
}
double N(int i,int r,double u,double *X)
{
double b1,c1,d1,b2,c2,d2;
if(r==0)
{
29

if(X[i]<=u && u<X[i+1])
return 1;
else
return 0;
}
else
{
b1 = (u - X[i])*N(i,r-1,u,X);
d1 = X[i+r] - X[i];
if(d1==0.0 && b1==0.0)
{
c1 = 0.0;
}
else
{
c1 = b1/d1;
}
b2 = (X[i+r+1] - u)*N(i+1,r-1,u,X);
d2 = X[i+r+1] - X[i+1];
if(d2==0.0 && b2==0.0)
{
c2 = 0.0;
}
else
{
c2 = b2/d2;
}
return c1 + c2;
}
}
//----------------------struct a_b intersection_length(double a,double b,double c,double d)
{// пересечение отрезков
// a<=c && c<=b
double x;
30

double y;
struct a_b temp;
temp.flag = 0;
if(b<c)
{
temp.a = 0;
temp.b = 0;
temp.flag = 0;
return temp;
}
if(d<a)
{
temp.a = 0;
temp.b = 0;
temp.flag = 0;
return temp;
}
//x = max(a,c);
if(a>=c) x = a;
else

x = c;

//y = min(c,d);
if(b<=d) y = b;
else

y = d;

temp.a = x;
temp.b = y;
temp.flag = 1;
return temp;
}
//----------------------double* FindM(int m,int j)
{// находим матрицу M = [(Φj|Φj+1)]
int i;
int t_j_1 = pow(2,j-1);
31

int t_j = pow(2,j);
int k = m+1; // порядок сплайна
double *X;
double *Y;
// определяем узловые вектора
// begin
X = (double *)calloc(pow(2,j-1)+2*m+1,sizeof(double));
if(X==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
Y = (double *)calloc(pow(2,j)+2*m+1,sizeof(double));
if(Y==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
for(i=1;i<=k;i++)
{
X[i-1] = 0;
Y[i-1] = 0;
}
for(i=t_j+2*m+1;i>t_j+2*m+1 -k;i--)
{
Y[i-1] = 1;
}
for(i=t_j_1+2*m+1;i>t_j_1+2*m+1 -k;i--)
{
X[i-1] = 1;
}
for(i=1; i<=t_j-1 ;i++)
{
Y[i+k-1] = i/(double)t_j;
}
32

for(i=1; i<=t_j_1-1 ;i++)
{
X[i+k-1] = i/(double)t_j_1;
}
// end
double *M;
M = (double *)calloc((t_j_1+m)*(t_j+m),sizeof(double));
if(M==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
int n; // количество точек в квадратуре Гаусса
// n должно быть таким, чтобы квадратура Гаусса была точна для
// полинома степени 2*m
n = int(double(2*m+1)/double(2)) + 1;
double summa;
double *x;
double *x_copy;
double *w;
double *w_copy;
double step;
double beg;
int l;
x = (double *)calloc(n,sizeof(double));
if(x==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
x_copy = (double *)calloc(n,sizeof(double));
if(x_copy==NULL)
{
cout<<"\nerror in calloc!";
33

exit(1);
}
w = (double *)calloc(n,sizeof(double));
if(w==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
w_copy = (double *)calloc(n,sizeof(double));
if(w_copy==NULL)
{
cout<<"\nerror in calloc!";
exit(1);
}
BuildGaussLegendreQuadrature(n, x, w);
for(i=0;i<t_j_1+m;i++)
{
for(j=0;j<t_j+m;j++)
{
// M[(t_j_1+m)*i + j] = integral(N(i,m,u,X)*N(i,m,u,Y) du);
struct a_b t;
t = intersection_length(X[i],X[i+k],Y[j],Y[j+k]);
if(t.flag==0)
{
M[(t_j+m)*i + j] = 0;
}
else
{
//M[(t_j_1+m)*i + j] = 0;
M[(t_j+m)*i + j] = 0;
step = double(1)/(double)t_j;
for(beg = t.a; fabs(t.b - beg)>step/2.0 ;beg = beg+step)
{
for(l=0;l<n;l++)
{
34

x_copy[l] = x[l];
w_copy[l] = w[l];
}
TranslateGaussLegendrePoints(n, beg , beg+step, x_copy, w_copy);
summa = 0;
for(l=0;l<n;l++)
{
summa = summa +
w_copy[l]*N(i,m,x_copy[l],X)*N(j,m,x_copy[l],Y);
}
M[(t_j+m)*i + j] = M[(t_j+m)*i + j] + summa;
}
}
}
}
free(w_copy);
free(w);
free(x_copy);
free(x);
free(Y);
free(X);
return M;
}

35