Разработка алгоритмов анализа длинных последовательностей, создаваемых системами имитационного моделирования

Имитационное моделирование, принципы и алгоритм. Расстояние между строками и вычисление наибольшей общей подпоследовательности. Средства, используемые в разработке, требования к программе. Инструкция пользователю. Структура программы, создающей строки.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 25.05.2015
Размер файла 2,7 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Размещено на http://www.allbest.ru/

ВЫПУСКНАЯ КВАЛИФИКАЦИОННАЯ РАБОТА

Разработка алгоритмов анализа длинных последовательностей, создаваемых системами имитационного моделирования

Введение

программа моделирование имитационный пользователь

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

Для каждой задачи рассмотрены различные варианты ее решения, эффективность того или иного алгоритма, в конце будет описан алгоритм, выбранный для реализации на языках программирования Pascal и C. Выбор алгоритма основывается на затратах памяти, так как сеанс моделирования порождает строки, характеризующиеся большим объемом данных - в каждой строке не менее 1 элементов.

1. Общая информация

1.1 Имитационное моделирование

Имитационное моделирование является одним из самых мощных инструментов анализа при разработке сложных систем и анализе процессов их функционирования. Имитационное моделирование как метод научного исследования предполагает использование компьютерных технологий для имитации различных процессов или операций - моделирования. Результаты определяются случайным характером процессов. На основе этих данных можно получить устойчивую статистику [4].

Имитационное моделирование позволяет моделировать поведение системы во времени. Его используют, когда:

1. Дорого или невозможно экспериментировать на реальном объекте.

2. Невозможно построить аналитическую модель: в системе есть время, причинные связи, последствие, нелинейности, стохастические (случайные) переменные.

3. необходимо сымитировать поведение системы во времени [4].

Области применения:

- Бизнес-процессы

- Бизнес-симуляция

- Боевые действия

- Динамика населения

- Дорожное движение

- ИТ-инфраструктура

- Математическое моделирование исторических процессов

- Логистика

- Пешеходная динамика

- Производство

- Рынок и конкуренция

- Сервисные центры

- Цепочки поставок

- Уличное движение

- Управление проектами

- Экономика здравоохранения

- Экосистема

- Информационная безопасность

- Релейная защита [5]

1.2 Основные определения

Последовательность - упорядоченный набор элементов [1].

Алфавит - конечное непустое множество, описывающее природу элементов строки. Члены этого множества называются буквами [1]. В данной работе алфавит состоит из целых положительных чисел.

Строковая последовательность (строка для краткости) - частный случай строки, удовлетворяющий следующим правилам:

1. Каждый элемент последовательности имеет уникальную метку

2. Каждый элемент с некоторой меткой x (за исключением не более одного элемента, который называется самый левый элемент) имеет единственный предшествующий элемент с меткой p(x).

3. Каждый элемент с некоторой меткой x (за исключением не более одного элемента, который называется самый правый элемент) имеет единственный последующий элемент с меткой s(x).

4. Для любого элемента с меткой x, который не является самым левым, выполняется равенство x=s (p(x)).

5. Для любого элемента с меткой x, который не является самым правым, выполняется равенство x=p (s(x)).

6. Для двух различных элементов с метками x и y существует такое целое положительное число k, что x= [1].

В данной работе строковая подпоследовательность, удовлетворяющая правилам 1-6, трактуется как одномерный массив. Произвольную строку x на алфавите A в виде массива можно задать следующим образом - x: array [1..n] of A [1].

Строка x длины n и строка y длины m равны в том случае, если:

1. n=m

2. x[i]=y[i], [1]

Строка б является подстрокой в, если в=гбд.

б называется префиксом в, если в=бг. [3]

б называется суффиксом в, если в=гб. [3]

б называется бордером в, если б одновременно является и суффиксом и префиксом в [3].

Число p называется периодом строки б (n=|б|) если . Кратность - количество повторений периода [3].

2. Алгоритмы анализа длинных подпоследовательностей, создаваемых системами имитационного моделирования

2.1 Расстояние между строками

Постановка задачи. Для данных строк и , где ||, || > 0, и метрики d, задающей расстояния между строками, вычислить d(,) [6].

Расстояние Хемминга [Hamming, 1982].

Расстоянием Хемминга между двумя строками является число позиций, в которых не совпадают элементы сравниваемых строк. Если строки одинаковой длины и разрешена только операция замены, стоимость которой равна единице, то минимальная цена преобразования будет равна числу позиций, в которых не совпадают элементы [2].

Расстоянием Левенштейна [Levenshtein distance] называется минимальное количество операций вставки или удаления одного символа, замены одного символа на другой, необходимых для преобразования одной строки в другую [2].

Алгоритм Вагнера-Фишера [Wagner-Fischer]

Для нахождения редакционного расстояния был использован алгоритм Вагнера-Фишера с небольшим изменением. Алгоритм Вагнера - Фишера вычисляет редакционное расстояние, основываясь на наблюдении, что если мы зарезервируем матрицу для хранения редакционных расстояний между всеми префиксами первой строки и всеми префиксами второй, то мы можем посчитать все значения в матрице, «потоково» заполняя матрицу. В итоге расстоянием между двумя полными строками будет значение, которое будет посчитано последним [2]. Чтобы вычислить редакционное расстояние (расстояние Левенштейна) необходимо вычислить матрицу D. Определим возможные операции, которые могут быть использованы для преобразования одной строки в другую:

§ с (a, b) - цена замены символа a на символ b

§ с (е, b) - цена вставки символа b

§ с (a, е) - цена удаления символа a

и цену этих операций:

§ с (a, а) = 0

§ с (a, b) = 1 при a?b

§ с (е, b) = 1

§ с (a, е) = 1 [2]

Будем считать, что все c неотрицательны, а так же действует правило треугольника: если 2 последовательные операции можно заменить одной, то общая цена не ухудшается [2].

Пусть и - две строки (длиной M и N соответственно) над некоторым алфавитом, тогда расстояние Левенштейна d( можно подсчитать по следующей формуле: , где

где m (a, b) равно нулю, если a=b и единице в противном случае min (a, b, c) возвращает наименьший из аргументов. Шаг по i представляет собой удаление из первой строки, по j - вставку в первую строку, а шаг по i и j символизирует замену символа или отсутствие изменений [2].

Очевидно, справедливы следующие утверждения:

-

-

- d( [2]

Доказательство

Рассмотрим формулу более подробно. Очевидно, что расстояние Левенштейна между двумя пустыми строками будет равно нулю. Так же очевидно то, что чтобы получить пустую строку из строки длиной i, нужно совершить i операций удаления, а чтобы получить строку длиной j из пустой, нужно произвести j операций вставки.

Теперь рассмотрим нетривиальный случай, когда обе строки являются не пустым.

Заметим, что в оптимальной последовательности операций, их можно произвольно менять местами. Рассмотрим две последовательные операции:

· Две замены одного и того же символа - не оптимально (если мы заменили x на y, потом y на z, выгоднее было сразу заменить x на z).

· Две замены разных символов можно менять местами

· Два стирания или две вставки можно менять местами

· Вставка символа с его последующим стиранием - не оптимально (можно их обе отменить)

· Стирание и вставку разных символов можно менять местами

· Вставка символа с его последующей заменой - не оптимально (излишняя замена)

· Вставка символа и замена другого символа меняются местами

· Замена символа с его последующим стиранием - не оптимально (излишняя замена)

· Стирание символа и замена другого символа меняются местами [2]

Пусть кончается на символ «a», кончается на символ «b». Есть три варианта:

1. Символ «а», на который кончается , в какой-то момент был стёрт. Сделаем это стирание первой операцией. Тогда мы стёрли символ «a», после чего превратили первые i-1 символов в (на что потребовалось D (i-1, j) операций), значит, всего потребовалось D (i-1, j) +1 операций

2. Символ «b», на который кончается , в какой-то момент был добавлен. Сделаем это добавление последней операцией. Мы превратили в первые j-1 символов , после чего добавили «b». Аналогично предыдущему случаю, потребовалось D (i, j-1)+1 операций.

3. Оба предыдущих утверждения неверны. Если мы добавляли символы справа от финального «a», то, чтобы сделать последним символом «b», мы должны были или в какой-то момент добавить его (но тогда утверждение 2 было бы верно), либо заменить на него один из этих добавленных символов (что тоже невозможно, потому что добавление символа с его последующей заменой не оптимально). Значит, символов справа от финального «a» мы не добавляли. Самого финального «a» мы не стирали, поскольку утверждение 1 неверно. Значит, единственный способ изменения последнего символа - его замена. Заменять его 2 или больше раз не оптимально. Значит,

1. Если a=b, мы последний символ не меняли. Поскольку мы его также не стирали и не приписывали ничего справа от него, он не влиял на наши действия, и, значит, мы выполнили D (i-1, j-1) операций.

2. Если a?b, мы последний символ меняли один раз. Сделаем эту замену первой. В дальнейшем, аналогично предыдущему случаю, мы должны выполнить D (i-1, j-1) операций, значит, всего потребуется D (i-1, j-1)+1 операций.

Для примера возьмем строки } и Заполненный массив стоимостей для данных строк будет выглядеть следующим образом (Рисунок 1):

Рисунок 1 - Заполненный массив стоимостей для строк } и

Минимальная стоимость преобразования находится в ячейке D [m, n], где m - длина строки n - длина строки .

Алгоритм в выше описанном виде требует O (m*n) операций и такие же емкостные затраты. Последнее являлось стимулом к изменению алгоритма, так как для нахождения расстояния Левенштейна для строк длинной в потребуется около38 гигабайт памяти в Pascal и в С. Поскольку для вычисления следующей строки матрицы D требуется только предыдущая, было решено использовать массив, содержащий предыдущую строку, из которой вычисляется текущая, и, собственно, текущая строка. После модернизации алгоритм выглядит следующим образом:

1. На первом шаге заполняем первую строку матрицы по формуле

2. Счетчик строк равен 0

3. На основе первой строки, используя формулу, заполняем вторую строку

4. Увеличиваем счетчик строк на 1

5. Если он равен длине второй полной строки, то расстояние Левенштейна равно последнему элементу матрицы D, если нет, то переходим на следующий шаг

6. Сдвигаем значения второй строки матрицы в первую D [0, j]=D [1, j]

7. Переходим к шагу 2

После изменения классического алгоритма временная сложность O (m*n) при памяти O (min(n, m)).

После модернизации будет происходить следующее:

1) Заполняем первые 2 строки, счетчик строк равен 1 (Рисунок 2).

Рисунок 2 - Массив стоимостей на первом шаге

2) Поскольку 1?16 сдвигаем значения из второй строки в первую и вычисляем новые значения второй строки, счетчик строк увеличиваем на 1 (Рисунок 3):

Рисунок 3 - Массив стоимостей на втором шаге

3) 2?16, снова сдвигаем значения, вычисляем новые и увеличиваем счетчик строк (Рисунок 4):

Рисунок 4 - Массив стоимостей на 3 шаге

4) 3?16, сдвигаем строки, вычисляем значения 2 строки, увеличиваем счетчик строк (Рисунок 5):

Рисунок 5 - Массив стоимостей на 4 шаге

5) 4?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 6):

Рисунок 6 - Массив стоимостей на 5 шаге

6) 5?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 7):

Рисунок 7 - Массив стоимостей на 6 шаге

7) 6?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 8):

Рисунок 8 - Массив стоимостей на 7 шаге

8) 7?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 9):

Рисунок 9 - Массив стоимостей на 8 шаге

9) 8?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 10):

Рисунок 10 - Массив стоимостей на 9 шаге

10) 9?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 11):

Рисунок 11 - Массив стоимостей на 10 шаге

11) 10?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 12):

Рисунок 12 - Массив стоимостей на 11 шаге

12) 11?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 13):

Рисунок 13 - Массив стоимостей на 12 шаге

13) 12?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 14):

Рисунок 14 - Массив стоимостей на 13 шаге

14) 13?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 15):

Рисунок 15 - Массив стоимостей на 14 шаге

15) 14?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 16):

Рисунок 16 - Массив стоимостей на 15 шаге

16) 15?16, сдвигаем строки, вычисляем новые значения, увеличиваем счетчик строк (Рисунок 17):

Рисунок 17 - Массив стоимостей на 16 шаге

16=16, расстояние Левенштейна вычислено.

Так же редакционное расстояние может быть вычислено и с помощью алгоритма нахождения наибольшей общей подпоследовательности - алгоритма Хиршберга (подробно описан далее). Временная и емкостная сложность этого алгоритма идентичны соответствующим сложностям измененного алгоритма Вагнера-Фишера. Модернизированный алгоритм Вагнера-Фишера был выбран поскольку он более прост в реализации, а так же алгоритм Хиршберга был реализован для поиска LCS.

2.2 Вычисление наибольшей общей подпоследовательности

Постановка задачи. Для данных строк и (||, || > 0) найти |LCS(,)|, где LCS(,) - самая длинная общая подпоследовательность (longest common subsequence) и [6].

Подпоследовательность последовательности X получается в результате удаления нескольких элементов (не обязательно соседних) из последовательности X. Имея две подпоследовательности X и Y, наибольшая общая подпоследовательность определяется как самая длинная последовательность, являющаяся подпоследовательностью как , так и . Например, для последовательностей={1, 3, 6, 2, 1, 1, 8} и ={1, 6, 2, 8, 9} LCS будет равна {1, 6, 2, 8}. Следует отметить, что наибольших общих подпоследовательностей, может быть несколько. [2]

Существуют множество различных подходов к решению данной задачи, были рассмотрены следующие: полный перебор, алгоритм Нидлмана-Вунша, алгоритм Ханта-Шуманского, алгоритм Хишберга.

Полный перебор

При полном переборе может быть осуществлен различными способами: можно перебирать варианты подпоследовательности, варианты вычеркивания из данных подпоследовательностей и т.д. Но какой бы подход не был использован, время работы программы будет экспонентой от длины строки.

Алгоритм Нидлмана-Вунша [Needleman-Wunsch]

Данный подход заключается в поэтапном заполнении матрицы, где строки являются элементами а столбцы элементы . При заполнении действуют два правила:

1. Если элемент =, то в ячейке (i, j) записывается значение ячейки (i-1, j-1) с добавлением единицы.

2. Если элемент ?, то в ячейку (i, j) записывается максимум из значений (i-1, j) и (i, j-1) [7].

Заполнение происходит в двойном цикле по i и j с увеличением значений на 1, таким образом, на каждой итерации нужные на этом шаге значения ячеек уже вычислены. После того, как произойдет полное заполнение матрицы, соединив ячейки, в которых происходило увеличение значений, мы получим искомую НОП. Соединять при этом нужно двигаясь от максимальных индексов к минимальным, если в ячейке увеличивалось значение, то добавляем соответствующий элемент к LCS, если нет, то двигаемся вверх или влево в зависимости от того где находится большее значение в матрице [7] (Рисунок 18, 19).

Рисунок 18 - Заполненная матрица похожести для строк и

Рисунок 19 - Выделение LCS

Временная сложность алгоритма - O (m*n), по памяти O (m*n). Очевидно, что для работы с большими объемами данных он не подходит, так будет требоваться много памяти.

Алгоритм Ханта-Шиманского [Hunt, Szymanski]

Метод выделения LCS для двух входных строк и Ханта и Шиманского эквивалентен нахождению максимального монотонно возрастающего пути в графе, состоящего из точек (i, j), таких, что = . На рисунке 20 иллюстрируется LCS (longestsunsequense, needlemanwunsh)=neeun, определенная с помощью «наибольшей» строго убывающей и непрерывной ломаной линии, проходящей через узлы сетки, представляющие совпадения = [1].

Рисунок 20 - LCS, определенная с помощью «наибольшей» строго убывающей и непрерывной ломаной линии

Ключевым в методе является массив значений. Установим некоторые свойства массива j, которые будут использованы в данном алгоритме [1].

Лемма 1. Для всех целых i1.. и h выполняются нервенства:

j [i ? 1, h ? 1] + 1 ? j [i, h] ? j [i ? 1, h]. [1]

Доказательство тривиально при i = 1, поэтому будем считать, что i > 1.

Заметим, что если элемент j [i?1, h] определен, то также определены j [i ? 1, h ? 1] и j [i, h]. Если подстроки [1..i ? 1] и [1..j [i ? 1, h]] имеют LCS длиной h, тогда (по определению массива j) подстроки [1..i] и [1..j [i ? 1, h]] имеют общую подпоследовательность длиной не менее h. Отсюда следует, что j [i, h] ? j [i ? 1, h] [1].

Далее, поскольку элемент j [i, h] определен, подстроки [1..i] и [1..j [i, h]] имеют общую подпоследовательность длиной h. Удалив самые правые буквы из этих подстрок, можно уменьшить длину общей подпоследовательности не более, чем на единицу. Поэтому подстроки [1..i ? 1] и [1..j [i, h] ? 1] имеют общую подпоследовательность и, следовательно, LCS длиной не менее h?1. Это означает, что j [i ? 1, h ? 1] ? j [i, h]. Что и требовалось доказать [1].

Лемма 2. Для всех целых i?1.. и h?1..|LCS( [1..i],)| значение j [i, h] вычисляется по следующему правилу:

j [i, h] равняется наименьшему целому ? j [i ? 1, h ? 1] + 1..j [i ? 1, h], такому, что [i] = [] (если такое существует); j [i, h] = j [i ? 1, h], если такого не существует [1].

Доказательство. Сначала рассмотрим, как более простой, второй вариант вычисления j [i, h], когда такого не существует. Поскольку j [i, h] определяет наименьший префикс строки , то самым правым элементом в LCS для подстрок [1..i] и [1..j [i, h]] будет буква [j [i, h]]. Вследствие леммы 1 j [i, h] ?j [i ? 1, h ? 1] + 1..j [i ? 1, h]. Но по нашему предположению не существует из этого интервала, такого, что [] = [i], поэтому [i] ? [j [i, h]]. Следовательно, та же самая LCS будет общей подпоследовательностью и для подстрок [1..i ? 1] и [1..j [i, h]] - в таком случае j [i, h] ? j [i ? 1, h]. Вследствие леммы 1 справедливо также обратное неравенство. Отсюда делаем заключение, что j [i, h] = j [i ? 1, h] [1].

Теперь предполагаем, что существует наименьшее целое из интервала j [i ? 1, h ? 1] + 1..j [i ? 1, h], такое, что [i] = []. Из этого предположения вытекает, что подстроки [1..i] и [1..] имеют общую подпоследовательность, состоящую из LCS( [1..i?1], [1..j [i?1, h?1]]) длиной h?1 и буквы x 2 []. Отсюда заключаем, что j [i, h] не превышает Теперь покажем, что j [i, h] не меньше . Предположим, что имеет место строгое неравенство j [i, h] < . Тогда, вследствие леммы 1, j [i ? 1, h ? 1] <j [i, h] < . Поскольку - это наименьшее целое из интервала j [i ? 1, h ?1]+1..j [i ? 1, h], такое, что [i] = [], то отсюда заключаем, что [i] ? [j [i, h]]. Так как по условию |LCS( [1..i], [1..j [i, h]])| = h, поэтому подстроки [1..i ? 1] и [1..j [i, h]] также имеют общую подпоследовательность длиной h. Следовательно, j [i ? 1, h] ? j [i, h + 1], а лемма 1 уточняет, что j [i?1, h] = j [i, h+1]. Но это невозможно, поскольку, как мы видели, j [i, h + 1] < < j [i ? 1, h]. Это противоречие и доказывает, что j [i, h] не меньше . Имея также доказанным утверждение, что j [i, h] не превышает , делаем вывод, что j [i, h] = . Теорема доказана [1].

Этот алгоритм можно описать в виде последовательности таких шагов.

Шаг 1. Вычисление массива MATCHLIST

Из леммы 2 следует, что для вычисления значений j [i+1, 0..n-1] на основе значений j [i, 0..n-1] надо для каждого значения i ? 1.. знать такую позицию j в строке , для которой [i] = [j]. Эту информацию в подходящей форме будем получать из массива MATCHLIST, в котором каждая позиция i является указателем на список значений j, таких, что [i] = [j]. Для дальнейших вычислений на шаге 3 желательно, чтобы эти значения j были упорядочены в убывающем порядке [1].

Шаг 2. Начальное задание значений массива

Полагаем [0] < 0 и для всех h?1.. полагаем [h] < +1 (тем самым показываем, что элементы [h] не определены). [1]

Шаг 3. Вычисление значений j [i, h] на основе значений j [i?1, h], i = 1,2,…,. Эти вычисления выполняются путем замены значений в предыдущем массиве j [0..], соответствующем номеру i ? 1, на аналогичные значения, соответствующие номеру i. Напомним (лемма 2), что j [i, h] = j [i ? 1, h] (тогда значение [h] не изменяется) только в том случае, если не существует номера из интервала j [i ? 1, h ? 1] + 1..j [i ? 1, h], такого, что [i] = []. Поэтому для текущего значения i и для каждого значения ?MATCHLIST[i] по значениям массива надо найти такое значение , что [ ? 1] + 1 ? ? []. Тогда, согласно лемме 2, [] < . Каждое такое присвоение, которое изменяет значение [], соответствует одной из r точек сетки (как показано на рисунке выше), где [i] = []. Это присвоение можно интерпретировать как создание конечного узла (листа) (i, , LINK[ ? 1]) на синтаксическом дереве, где LINK[? 1] - указатель на узел, созданный ранее при присвоении для [ ? 1]. (Для = 1 полагаем, что LINK[ ? 1] = NULL.) Когда лист создан, указатель на него хранится в LINK[]. После обработки всех i ? 1..n 1 наибольшее значение h, такое, что [h] < + 1, равняется длине LCS(, ), а LINK[h] указывает путь длиной h по дереву от листа до корня. Этот путь и определяет LCS. [1]

Шаг 4. Запись LCS (x 1, x 2) в обратном порядке.

На дереве находится первый узел, который соответствует наибольшему значению h, такому, что [h] < + 1. Затем записывается путь от этого узла до корня дерева - это и будет LCS(, ), записанная в обратном порядке.

Пример работы алгоритма Ханта-Шуманского.

={longestsubsequence}

={needlemanwunsh}

Заполненный массив MATCHLIST (Рисунок 21):

Рисунок 21 - Значения массива MATCHLIST

Значения элементов массива после i-й итерации следующие (Рисунок 22):

Рисунок 22

Время работы этого алгоритма равно O((r + n) log n), где n - длина большей последовательности, а r - количество совпадений, в худшем случае при r = n*n, мы получаем время работы хуже, чем в предыдущем варианте решения. Требования по памяти остаются порядка O (r+n).

Алгоритм Хиршберга [Hirschberg]

Хиршберг предложил алгоритм непосредственного вычисления LCS(за время порядка O (n*m) без явного вычисления массива стоимостей. Он так же показал, что этот алгоритм требует память объемом только O (min(m, n) [2]. Для реализации был выбран этот алгоритм.

Теорема 1. (1) [10].

Обозначим длину LCS для суффиксов (i+1, m) и (j+1, n) как =|LCS((i+1, m),(j+1, n))| (2), где: - первая строка; n - длина первой строки; - вторая строка; m - длина второй строки [10].

Для 0<j<m значения являются длинами LCS префикса (1, i) и различных префиксов . Аналогично, значения являются длинами LCS обращенного суффикса и различных префиксов обращенной строки [10].

Определим формулой: , тогда для 0< i <n [10].

Доказательство: пусть для j=. - произвольная LCS((i, j),(i,), - произвольная LCS((i+1, n),(, тогда с= является общей подпоследовательностью и , ее длина равна M. Таким образом, ?(3) [10].

Пусть - произвольная LCS(,), тогда является подпоследовательностью , равной S1S2, где S1 - подпоследовательность (1, i), а S2 - подпоследовательность (i+1, n). Таким образом, существует значение , такое, что S2 является подпоследовательностью (1,), а S2 - подпоследовательностью (, m) [10].

Длины S1 и S2 удовлетворяют следующим условиям:

|S1|?|LCS((1, i),(1,))| ? согласно (1).

|S2|?|LCS((i+1, n),(+1, m))| ? согласно (2)

Таким образом: (4)

Из неравенств (3) и (4) получаем . Что и требовалось доказать [10].

Алгоритм:

1) Сначала необходимо проверить не тривиальный ли случай. Если хотя бы одна из строк пуста, то в качестве результата возвращается пустая строка. Если хотя бы одна из строк односимвольная, то проверяется, содержится ли этот символ в другой строке. Если содержится, то в качестве результата возвращается этот символ, в противном случае - пустая строка [10].

2) Если случай нетривиальный, первая строка делится пополам, после чего ищутся длина наибольшей общей подпоследовательности ее первой половины и префиксов второй строки различной длины и длина LCS ее второй половины и суффиксов второй строки различной длины [10].

3) По теореме 1 ищется первая позиция во второй строке К такая, что LCS первой половины первой строки и первых К символов второй строки в соединении с LCS второй половины первой строки и последних m-K (m - длина второй строки) символов второй строки равна требуемой LCS первой и второй строк.

4) Найденное К используется для решения двух подзадач, для которых так же вычисляются шаги 1-4 [10].

5) В конечном итоге все подзадачи сводятся к тривиальным и их результаты объединяются [10].

Вычисление длины LCS.

Так как длина LCS любой строки и пустой строки равна 0, значения границ массива инициализируются нулями. В позиции (i, j), если , мы получаем новую LCS, присоединяя этот символ к текущей LCS префиксов x (1, i-1) и y (1, j-1), то есть L (i, j)=L (i-1, j-1)+1. Иначе длина текущей LCS просто равна максимальному из предыдущих значений. [10]

Так как для расчетов нам нужна только предпоследняя строка, можно сократить расходы памяти, храня только текущую и предыдущую строки. [10]

Эта задача решается для двух половин первой строки.

1. Заполняем вторую строку 0 (Рисунок 23).

Рисунок 23 - Состояние массива на 1 шаге

2. Сдвигаем строку вверх (Рисунок 24).

Рисунок 24 - Состояние массива на 2 шаге

Изначально после сдвига в первой строке будут 0, затем другие числа.

3. Рассчитываем текущую строку.

4. Пока не достигнем длины первой строки (первая строка в данном случае одна из половин ) повторяем шаги 2-3 [10].

После решения двух задач (для каждой половины ) получим два вектора . ) и будет длиной LCS для строк и [10].

2.3 Вычисление длины периода и количество повторений периода (кратность)

Постановка задачи. Для данной строки , || = n > 0, найти самую длинную подстроку, встречающуюся в больше одного раза. Самая длинная повторяющаяся подстрока - это самая длинная из строк максимальной длины x, такая, что = uxvxw для некоторых строк u, v и w, где |u|0, |v|0 и |w| 0 [6]. Если же существует несколько периодов максимальной длины, то выбрать период с максимальной кратностью.

Алгоритм Крочемора [Crochemore]

Основная идея алгоритма Крочемора: выполнение декомпозиции подпоследовательностей на каждом уровне ? только относительно последовательностей, которые на этом уровне являются малыми [1].

На уровне ?? 2 разбиение множества позиций строки на отдельные непересекающиеся множества (последовательности) - это декомпозиция разбиения на уровне ? ? 1 [1].

В декомпозиции последовательности на подпоследовательности (, , …, ) (q ?1) назовем одну подпоследовательность с самым большим количеством элементов большой, а остальные q - 1 подпоследовательностей - малыми. Для уровня ? = 1 каждая последовательность малая [1].

Основная сложность в реализации этого алгоритма заключается в структурах данных, обеспечивающих время выполнения алгоритма за O (n log n) [1].

Структуры необходимые для обеспечения работы алгоритма за O (n log n), классифицированные в соответствии с их основными функциями:

1. Запись текущей последовательности для каждой позиции в строке x.

- Массив SEQ [?..n]: SEQ[i] содержит индекс текущей последовательности, которой принадлежит i-я позиция в строке x.

- Массив SEQLIST [1..2n]: SEQLIST[j] - указатели на двусвязный список позиций, принадлежащих последовательности с индексом j и расположенных в порядке их возрастания.

- Массив SEQSIZE [1..2n]: SEQSIZE[j] равно количеству позиций в последовательности с индексом j, т.е. количеству позиций в списке, на который указывает элемент SEQLIST[j].

- Стек INDEXSTACK: стек неиспользованных индексов последовательностей, инициализированный для размещения индексов 1,2,…, 2n [1].

Всякий раз, когда необходимо сформировать новую последовательность или на уровне 1, или в качестве части декомпозиции существующей последовательности, из стека INDEXSTACK извлекается индекс последовательности. Если последовательность становится пустой в результате ее декомпозиции на подпоследовательности, индекс последовательности возвращается в стек INDEXSTACK. Всякий раз, когда к последовательности с индексом j добавляется позиция i, выполняются операторы присваивания SEQ[i] < j; SEQSIZE[j] < SEQSIZE[j] + 1, а номер i добавляется в конец списка SEQLIST[j]. Если i-я позиция удаляется из последовательности с индексом j в результате его вхождения в подпоследовательность с индексом j, выполняется присваивание SEQSIZE[i] <SEQSIZE[j] ? 1, а i-я позиция удаляется из списка SEQLIST[i].

2. Управление малыми последовательностями.

- Очередь SMALL: очередь индексов j малых последовательностей, организованная таким образом, что (для ? > 1) все имеющиеся в ней малые последовательности являются подпоследовательностями одной и той же последовательности предыдущего уровня.

- Очередь QUEUE: очередь пар (i, j), где i - позиция в малой последовательности с индексом j, которая должна использоваться для декомпозиции текущего уровня; для каждого j позиции i располагаются в возрастающем порядке.

- Массив LASTSMALL [1..2n]: LASTSMALL[] = j > 0 тогда и только тогда, когда j - это индекс малой последовательности, использованной последней по времени для декомпозиции последовательности с индексом [1].

Для ? = 1 очередь SMALL содержит все индексы, которые первоначально вычисляются в соответствии с каждым символом строки x; для ? > 1 очередь SMALL создается повторно путем просмотра подпоследовательностей, тех последовательностей, для которых была выполнена декомпозиция на уровне ?, и добавлением к очереди SMALL всех подпоследовательностей, кроме единственной «большой» подпоследовательности (см. описание массивов SPLIT и SUBSEQ в следующем пункте). Для каждого элемента j в очереди SPLIT эти действия можно выполнить за один просмотр списка, на который указывает элемент SUBSEQ[j]. Таким образом, очередь SMALL модифицируется за время, пропорциональное количеству имеющихся в ней элементов [1].

Очередь QUEUE создается в начале обработки для каждого уровня ? > 1 путем удаления элементов из очереди SMALL. Для каждой последовательности j, обнаруженной в очереди SMALL, извлекаются найденные в списке SEQLIST[j] позиции i > 1, а элементы (i, j) добавляются к очереди QUEUE. Основная обработка каждого из уровней ? > 1 выполняется после того, как из очереди QUEUE один за другим удаляются элементы (i, j). Для каждой удаленной позиции I последовательность = SEQ [i?1] является той последовательностью, которая будет подвергнута декомпозиции. Следовательно, если LASTSMALL[] ?j, то последовательность была последней по времени подвергнута декомпозиции относительно некоторого другого класса, поэтому необходим новый индекс последовательности. Соответственно с этим устанавливаем LASTSMALL[] < j, извлекаем из стека INDEXSTACK следующий номер последовательности и добавляем его к списку, на который указывает SUBSEQ[ [1].

3. Организация подпоследовательностей.

- Массив SPLITFLAG [1..2n]: SPLITFLAG[j] = 1 тогда и только тогда, когда последовательность с индексом j будет подвергнута декомпозиции на текущем уровне (с использованием последовательностей, которые хранятся в очереди SMALL).

- Список SPLIT: список последовательностей j, которые будут подвергнуты декомпозиции на текущем уровне (т.е. для которых SPLITFLAG[j] = 1).

- Массив SUBSEQ [1..2n]: при декомпозиции последовательности j на текущем уровне SUBSEQ[j] указывает на список индексов подпоследовательностей последовательности j, первым элементом в этом списке будет сам индекс j [1].

Все эти структуры данных инициализируются для каждого уровня ? > 1 одновременно с формированием из очереди SMALL очереди QUEUE. В частности, когда (i, j) добавляется к очереди QUEUE, известно, что i будет использоваться для декомпозиции последовательности , в которой имеется позиция i ? 1 ( = SEQ [i ? 1]). Поэтому, если SPLITFLAG[] = 0, то в это время хранится в SPLIT - списке последовательностей, которые подлежат декомпозиции на уровне ?, в то время как выполняются следующие присваивания:

SPLITFLAG[] < 1; SUBSEQ[] < < >; LASTSMALL[] < 0. [1]

По завершении обработки каждого уровня ? (? > 1) последовательности j удаляются один за другим из списка SPLIT: если SEQSIZE[j] = 0, то эта последовательность уже пуста и поэтому ее индекс больше не требуется. Поэтому j помещается в стек INDEXSTACK и удаляется из списка, на который указывает SUBSEQ[j]. [1]

4. Вычисление кратных строк.

· Массив GAP [?..n]: элемент GAP[i] равен положительной разности между i-й позицией в строке x и следующей большей позицией в той же самой последовательности на текущем уровне; если нет большей позиции, то GAP[i] = ?.

· Массив GAPLIST [?..n]: GAPLIST[g] указывает на двусвязный список i-x позиций, для которых GAP[i] = g.

Для ? = 1 массивы GAP и GAPLIST инициализируются непосредствен - но путем обработки каждого списка, на который указывает SEQLIST[j], где j = 1,2,…,, вычисляя GAP[i] для каждой i-й позиции в списке SEQLIST[j], затем добавляя i-ю позицию к списку, на который указывает GAPLIST [GAP[i]] [1].

При ? > 1 массивы GAP и GAPLIST обновляются при удалении элемента (i, j) из очереди QUEUE, что приводит к декомпозиции последовательности = SEQ [i?1], при которой позиция i?1 в SEQLIST[] будет перемещена в конец SEQLIST[]. Поскольку списки, на которые указывают и SEQLIST и GAPLIST, двусвязные, повторное вычисление элементов массива GAP, необходимое из-за удаления позиции i ? 1 из одного списка и ее вставки в другой список, можно выполнить непосредственно [1].

Мы видим, что, после создания последовательностей для уровня ?, каждый элемент в GAPLIST[?] описывает две идентичные подстроки строки x длиной ?, первые буквы которых находятся друг от друга на расстоянии ?, другими словами, имеем квадрат этой подстроки.

Для этого алгоритма обязательна упорядоченность алгоритма. Временная сложность - O (n log n), емкостная - O(n).

Алгоритм Мейна-Лоренца [Main, Lorentz]

Для реализации был выбран алгоритм Мейна-Лоренца, поскольку он работает с неупорядоченным алфавитом, но все равно выполняется за время порядка O(nlogn), а так же не требует реализации большого количества сложных структур данных. Это алгоритм типа «разделяй и властвуй», который вычисляет все квадраты подстрок, следовательно, все кратные строки в строковой последовательности x = x [?..n], распределяя их на три различных класса.

· Класс С1 - класс квадратов w 2, которые начинаются и заканчиваются в подстроке x [1..], т.е. = x [i..i + 2|w| ? 1], где i + 2|w| ? 1 ?.

· Класс С2 - класс квадратов, которые начинаются и заканчиваются в подстроке x[ + 1..n], т.е. = x [i..i + 2|w| ? 1], где i >

· Класс С3 - класс квадратов , которые начинаются в подстроке x [1..] и заканчиваются в подстроке x[+1..n], т.е. здесь = x [i..i+2|w|?1], где 1 ? i ? и < i + 2|w| ? 1 ? n.

Обозначим u = [1..] и v = x[+1..n]. Основная идея алгоритма Мейна-Лоренца заключается в вычислении квадратов подстрок в строке x = uv на основе эффективного вычисления новых квадратов класса C3 [1].

Для описания процедуры C3 (u, v) (ищет квадраты класса С3) вначале отметим, что новые квадраты в строке uv могут быть двух видов:

· правый квадрат, когда второе вхождение w в строку uv полностью находится в подстроке v;

· левый квадрат, когда в подстроке u имеется непустой префикс второго вхождения w [1].

Например, подстроки u = aba и v = ba порождают правый квадрат = (ba и левый квадрат = (ab [1].

Процедура C3 реализуется как комбинация процессов вычисления правых и левых квадратов. Пусть u = u [?..] и v = v [?..]. Чтобы в uv найти все правые квадраты, необходимо вычислить два массива. Массивы можно кратко определить следующим образом:

· Массив LP [2.. + 1]: для каждой позиции i = 2,3,…, строки v LP[i]=lcp (v[i..], v), при этом для i = + 1 принимаем, что LP[i] = 0.

· Массив LS [1..]: для каждой позиции i = 1,2,…, строки v LS[i]=lcs (v[1..i], u) [1].

Например, для строки v = abaababa массив LP [2..9] = 01303010. Если u=abaab, тогда LS [1..8] = 02005020. Приближенно можно сказать, что массив LP рекуррентно определяет максимальные длины префиксов v внутри самой v, а массив LS определяет вхождения суффиксов u максимальной длины в строку v [1].

Лемма 1. Пусть u [1..] и v [1..] - произвольные непустые строки и пусть p ? и I ? p..2p?1 - положительные целые числа. В uv имеется квадрат длиной 2p, заканчивающийся на i-й позиции строки v, тогда и только тогда, когда: 2p ? LS[p] ?i ?p + LP [p + 1] [1].

Доказательство. В строке uv для любого квадрата w 2 длиной 2p, заканчивающегося на i-й позиции, первое вхождение w должно начинаться с буквы u[ ?2p + i + 1], а второе вхождение - с буквы v [i ? p + 1]. Поэтому такой квадрат имеет «право на существование» тогда и только тогда, когда: u[ ? 2p + i + 1..] v [1..i ? p] = v [i ? p + 1..i]; т.е. только тогда, когда:

· u[ ? 2p + i + 1..] = v [i ? p + 1..p] и

· v [1..i ? p] = v [p + 1..i].

Отметим, что если i = p, вследствие чего квадрат формируется из суффикса w строки u и префикса w строки v, второе условие сводится к утверждению, что е = е, и поэтому становится ненужным. [1]

Первое условие, которое говорит о том, что строка v [i?p+1..p] длиной 2p?I является суффиксом строки u, выполняется только тогда, когда LS[p] ? 2p ?i, т.е. только в случае выполнения неравенства 2p ? LS[p]? i. Второе условие эквивалентно неравенству i ? p + LP [p + 1], что и завершает доказательство [1].

Правый квадрат возможен только для тех значений p и i, которые удовлетворяют условиям леммы 1. Кроме того, для каждого допустимого значения p ? 1.. соответствующие допустимые значения величины i могут находиться только в интервале 2p ? LS[p]..p + LP [p + 1].

Для вычисления левых квадратов нужны аналогичные массивы:

· Массив L [2..]: для каждой позиции i = 2,3,…, строки u L [i]= lcp (u[i..], v).

· Массив L [1.. ? 1]: для каждой позиции i = 1,2,…, ? 1 строки u

L [i] = lcs (u[?..i], u) [1].

Временная сложность алгоритма - O (n log n), емкостная - O(n) [1].

3. Описание программы

3.1 Средства, используемые в разработке

Microsoft Visual Studio

Visual Studio - это полный набор инструментов и служб для разработки приложений для настольных компьютеров, Интернета, мобильных устройств и облачных систем. Visual Studio позволяет создавать как консольные приложения, так и приложения с графическим интерфейсом. Основными языками Visual Studio являются Visual C#, Visual Basic и C++. [11]

Microsoft Visual Studio объединяет в себе огромное количество функций, позволяющих осуществлять разработки для Windows всех версий, в том числе и 8, Интернета, SharePoint, различных мобильных устройств и облачных технологий. Visual Studio включает в себя редактор исходного кода с поддержкой технологии IntelliSense и возможностью рефакторинга кода. Встроенный отладчик может работать как отладчик уровня исходного кода, так и как отладчик машинного уровня. Остальные встраиваемые инструменты включают в себя редактор форм для упрощения создания графического интерфейса приложения, веб-редактор, дизайнер классов и дизайнер схемы базы данных. Visual Studio позволяет создавать и подключать сторонние дополнения (плагины) для расширения функциональности практически на каждом уровне [11].

Плюсом Microsoft Visual Studio для разработки на С является наличие стандартной утилиты анализа кода Code Analysis. Эта утилита позволяет быстро отыскивать «слабые» места в коде. Например, использование не безопасных функций, утечки памяти, выход за границы массива.

В этой работе для создания проекта используется Visual Studio Professional 2013.

В качестве языка разработки используется C.

PascalABC.NET

PascalABC.NET - это среда разработки, поддерживающая язык Pascal нового поколения, включающий классический Pascal, большинство возможностей языка Delphi, а также ряд собственных расширений. Он реализован на платформе Microsoft.NET и содержит все современные языковые средства: классы, перегрузку операций, интерфейсы, обработку исключений, обобщенные классы и подпрограммы, сборку мусора, лямбда-выражения, средства параллельного программирования [8].

PascalABC.NET является мультипарадигменным языком: на нем можно программировать в структурном, объектно-ориентированном и функциональном стилях [8].

PascalABC.NET - это также простая и мощная интегрированная среда разработки, поддерживающая технологию IntelliSense, содержащая средства автоформатирования, встроенный отладчик и встроенный дизайнер форм [8].

Самое большое преимущество среды разработки PascalABC.NET перед другими является наличие удобного режима отладки. В этом режиме пользователю доступны следующие средства отладки: возможность задания точек останова программы, пошаговое выполнение кода программы со входом в подпрограмму и без входа в подпрограмму, а так же просмотр текущих значений глобальных и локальных переменных.

Динамическое программирование

Динамическое программирование - метод решения сложных задач путем их разбиения на более простые. Основная идея: основная задача разбивается на меньшие отдельные задачи (подзадачи), они решаются, после чего необходимо объединить решения подзадач в одно общее решение. Часто многие из этих подзадач одинаковы. Подход метода заключается в решении каждой подзадачи один раз. Это приводит к сокращению вычислений. Этот подход полезен в случаях, когда число повторяющихся подзадач экспоненциально велико [9].

Оптимальная подструктура в динамическом программировании означает, что оптимальное решение подзадач меньшего размера может быть использовано для решения исходной задачи. В общем случае мы можем решить задачу, в которой присутствует оптимальная подструктура, проделывая следующие три шага:

1. Разбиение задачи на подзадачи меньшего размера.

2. Нахождение оптимального решения подзадач рекурсивно, проделывая такой же трех шаговый алгоритм.

3. Использование полученного решения подзадач для конструирования решения исходной задачи [9].

Перекрывающиеся подзадачи в динамическом программировании означают подзадачи, которые используются для решения некоторого количества задач (не одной) большего размера (то есть мы несколько раз проделываем одно и то же), динамическое программирование пользуется следующими свойствами задачи:

· перекрывающиеся подзадачи;

· оптимальная подструктура;

· возможность запоминания решения часто встречающихся подзадач [9].

Динамическое программирование бывает двух видов:

· нисходящее динамическое программирование: задача разбивается на подзадачи меньшего размера, они решаются и затем комбинируются для решения исходной задачи. Используется запоминание для решений часто встречающихся подзадач.

· восходящее динамическое программирование: все подзадачи, которые впоследствии понадобятся для решения исходной задачи, просчитываются заранее и затем используются для построения решения исходной задачи. Этот способ лучше нисходящего программирования в смысле размера необходимого стека и количества вызова функций, но иногда бывает нелегко заранее выяснить, решение каких подзадач нам потребуется в дальнейшем [9].

3.2 Структура программы

Структура программы на языке Pascal

Структуры данных:

IntArray = array of longint - массив целых чисел.

IntMatrix = array of array of longint - массив массивов целых чисел.

Функции и процедуры:

function Min (X: integer; Y: integer) - функция возвращающая минимальное значение из пары чисел X и Y.

function Max (X: integer; Y: integer) - функция возвращающая максимальное значение из пары чисел X и Y.

function SubString (STR: IntArray; SubSTR: IntArray; FirstIndex: longint) - поиск индекса первого вхождения подмассива SubSTR в массив STR с элемента FirstIndex.

procedure ReadFromFile (file1: text; path: string; var S: IntArray) - процедура считывающая значения массива из файла. На вход подается переменная текстового файла file1, путь к файлу path, массив S, который будет заполняться.

procedure InitSEQ() - процедура, инициализирующая подпоследовательность. Изначально заполняет LCS -1, то есть LCS пуста.

procedure LCS_LENGTH (M: integer; N: integer; X: IntArray; Y: IntArray; var LL: IntArray) - Процедура, вычисляющая вектор, равный последней строке матрицы LCS. Входные параметры: X - первая строка, Y - вторая строка, LL - массив, в который будет помещен результат.

procedure FindMaxPeriod (STR: IntArray) - процедура, которая находит период максимальной длины. На вход подается массив STR, в котором содержится строка для которой необходимо произвести поиск.

procedure LCS (M: integer; N: integer; X: IntArray; Y: IntArray; var C: IntArray) - процедура, которая вычисляет LCS. На вход подаются: длина первой строки M; длина второй строки N; массив X, содержащий первую строку; массив Y, содержащий вторую строку; массив C, в который будет записываться LCS.

function EditDinst (D: IntMatrix; X: IntArray; Y: IntArray; i, j: longint) - функция, заполняющая массив стоимостей. На вход функция получает: массив стоимостей D; массив X, содержащий первую строку; массив Y, содержащий вторую строку; текущий элемент i в массиве X; текущий элемент j в массиве Y.

function Levenshtein (X: IntArray; Y: IntArray; l1: longint; l2: longint) - функция, вычисляющая расстояние Левенштейна. Входные параметры: массив X, содержащий первую строку; массив Y, содержащий вторую строку; длина первой строки l1; длина второй строки l2.

procedure PrintLCS() - выводит результат вычислений LCS.

procedure PrintEditDinst() - выводит результат вычислений расстояния Левенштейна.

procedure PrintLCSToFile() - записывает в файл результат вычислений LCS.

procedure PrintEditDinstToFile() - записывает результат вычисления редакционного расстояния.

Структура программы на языке C

Функции и процедуры:

int Min (int X, int Y) - возвращает минимальное значение из пары чисел X и Y.

int Max (int X, int Y) - возвращает максимальное значение из пары чисел X и Y.

long int subString (int str[], long int strSize, int subStr[], long int subStrSize, long int firstIndex) - поиск индекса первого вхождения подмассива subStr[], длинной subStrSize, в массив str[], длинной strSize, с элемента firstIndex.

int * lcsLength (long int m, long int n, int X[], int Y[], int LL[]) - вычисление вектора, который равен последней строке матрицы LCS. Входные параметры: X[] - первая строка; Y[] - вторая строка; LL[] - массив, в который будет помещен результат; m - длина первой строки; n - длина второй строки.

int * lcs (long int m, long int n, int X[], int Y[], int C[]) - вычисляет LCS. Входные параметры: длина первой строки m; длина второй строки n; массив X[], содержащий первую строку; массив Y[], содержащий вторую строку; массив C[], в который будет записываться LCS.

long int editDinst (long int D[], long int n, int X[], int Y[], long int i, long int j) - заполнение массива стоимостей D[]. n - длина второй строки; X[] - первая строка; Y[] - вторая строка; i - текущий элемент в массиве X; j - текущий элемент в массиве Y.

long int levenshtein (int X[], int Y[], long int l1, long int l2) - вычисление расстояния Левенштейна. X[] - первая строка; Y[] - вторая строка; l1 - длина первой строки; l2 - длина второй строки.

void findMaxPeriod (int str[], long int length) - нахождение периода максимальной длины. Входные параметры: str[] - строка, в которой необходимо найти период максимальной длины; length - длина строки.

void printLCSToFile (int C[]) - запись результата вычисления LCS в файл.

void printEDToFile (long int editDinstance) - запись результата вычисления расстояния Левенштейна в файл.

3.3 Описание и структура программы, создающей строки с заданными параметрами

Программа StringGenerator создана для генерации тестовых данных. Так как основная программа решает три задачи: нахождение расстояния Левенштейна двух строк, нахождение наибольшей общей подпоследовательности и нахождение максимального периода максимальной кратности (не меньше 2), то к тестовым данным были предъявлены следующие условия:

- Первая строка должна генерироваться с указанными параметрами периодичности и длины.

- Вторая строка должна получаться путем изменения указанного числа элементов первой строки.

Эти два условия позволяют создать тестовые данные одновременно для всех трех решаемых задач, так как:

- Заранее заданные параметры периодичности позволяют оценивать правильность определения наличия периода в строке (при этом стоит учитывать, что, например, период длины 3 кратности 4 будет определяться программой как период длины 6 кратности 2, и это правильно, так как программа ищет максимальный период).

- Получение второй строки путем изменения первой позволяет определять правильность нахождения расстояния Левенштейна и наибольшей общей подпоследовательности.


Подобные документы

  • Основы систематизации языков имитационного моделирования, моделирование систем и языки программирования. Особенности использования алгоритмических языков, подходы к их разработке. Анализ характеристик и эффективности языков имитационного моделирования.

    курсовая работа [1,4 M], добавлен 15.03.2012

  • Разработка решения задачи имитационного моделирования системы массового обслуживания (СМО), на примере склада продукции. Построение концептуальной модели системы. Сравнение результатов имитационного моделирования и аналитического расчета характеристик.

    курсовая работа [75,5 K], добавлен 26.06.2011

  • Характеристика функций имитационного моделирования. Знакомство с особенностями имитационного моделирования агрегированной системы массового обслуживания. Анализ программы GPSSWorld: рассмотрение возможностей, способы составления имитационной модели.

    курсовая работа [1,6 M], добавлен 27.05.2013

  • Сущность, принципы и описание методов и этапов имитационного моделирования. Процессы и применение дискретного и непрерывного алгоритма. Характеристика методов построения математических моделей для решения управленческих задач банковской системы.

    курсовая работа [80,5 K], добавлен 29.05.2014

  • Имитационное моделирование как один из наиболее широко используемых методов при решении задач анализа и синтеза сложных систем. Особенности имитационного моделирования систем массового обслуживания. Анализ структурной схемы системы передачи пакетов.

    курсовая работа [1,2 M], добавлен 28.05.2013

  • Моделирование концепта внешнего USB картридера с функцией USB и Ethernet hub с экраном, на котором располагается нужная пользователю информация. Особенности проектирования 3d модели USB гаджета: выбор программы и метода моделирования, разработка дизайна.

    контрольная работа [217,7 K], добавлен 18.01.2012

  • GPSS как один из эффективных и распространенных языков моделирования сложных дискретных систем. Возможности языка GPSS. Построение имитационной модели "Моделирование мини-АТС". Разработка программы работы диспетчерского пункта в торговом предприятии.

    курсовая работа [118,8 K], добавлен 19.01.2016

  • Использование языка GPSS для описания модели автосервиса, обслуживающего автомобили различных моделей с учетом их приоритета. Сущность и возможности имитационного моделирования. Разработка GPSS-модели функционирования ремонтных работ в автосервисе.

    курсовая работа [259,4 K], добавлен 08.05.2013

  • Применение метода имитационного моделирования с использованием генератора случайных чисел для расчета статистически достоверных переменных. Создание программы на языке GPSS. Результаты моделирования диспетчерского пункта по управлению транспортом.

    курсовая работа [399,9 K], добавлен 28.02.2013

  • Разработка имитационной модели "Перекресток" для анализа бизнес-процессов предприятия и принятия решения в сложных условиях. Алгоритм построения имитационной модели на основе CASE-средств. Обзор программного обеспечения для имитационного моделирования.

    дипломная работа [2,6 M], добавлен 22.11.2015

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.