- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Фундаментальные алгоритмы и структуры данных в Delphi - Джулиан Бакнелл
Шрифт:
Интервал:
Закладка:
begin
{передать сигнал о готовности к началу потребления данных}
FSyncObj.StartConsuming(FID);
{выполнить считывание начального буфера очереди}
Head := FBuffers.Head[FID];
{до тех пор, пока начальный буфер не пуст...}
while (Head^.bCount <> 0) do
begin
{выполнить запись блока из начального буфера очереди в поток}
FStream.Write(Head^.bBlock, Head^.bCount);
{переместить указатель начала очереди}
FBuffers.AdvanceHead(FID);
{обработка этого буфера завершена}
FSyncObj.StopConsuming(FID);
{передать сигнал о повторной готовности к началу потребления данных}
FSyncObj.StartConsuming(FID);
{выполнить считывание начального буфера очереди}
Head := FBuffers.Head[FID];
end;
{обработка последнего буфера завершена}
FSyncObj.StopConsuming(FID);
end;
И, наконец, рассмотрим подпрограмму копирования потоков, код которой показан в листинге 12.21.
Листинг 12.21. Копирование потоков с применением модели "производитель-потребитель"
procedure ThreadedMultiCopyStream(aSrcStream : TStream;
aDestCount : integer;
aDestStreams : PStreamArray);
var
i : integer;
SyncObj : TtdProduceManyConsumeSync;
Buffers : TQueuedBuffers;
Producer : TProducer;
Consumer : array [0..pred(MaxConsumers) ] of TConsumer;
WaitArray : array [0..MaxConsumers] of THandle;
begin
SyncObj nil;
Buffers nil;
Producer :=nil;
for i := 0 to pred(MaxConsumers) do
Consumer[i] := nil;
for i := 0 to MaxConsumers do
WaitArray[i] := 0;
try
{создать объект синхронизации}
SyncObj : * TtdProduceManyConsumeSync.Create(20, aDestCount);
{создать объект буфера с очередью}
Buffers := TQueuedBuffers.Create(20, aDestCount);
{создать поток производителя и сохранить его дескриптор}
Producer := TProducer.Create(aSrcStream, SyncObj, Buffers);
WaitArray[0] := Producer.Handle;
{создать потоки потребителей и сохранить их дескрипторы}
for i := 0 to pred(aDestCount) do
begin
Consumer [ i ] := TConsumer.Create(
aDestStreams^[i], SyncObj, Buffers, i);
WaitArray[i+1] := Consumer[i].Handle;
end;
{запустить потоки}
for i := 0 to pred(aDestCount) do
Consumer[i].Resume;
Producer.Resume;
{ожидать завершения потоков}
WaitForMultipleObjects(l+aDestCount, @WaitArray, true, INFINITE);
finally Producer.Free;
for i := 0 to pred(aDestCount) do
Consumer[i].Free;
Buffers.Free;
SyncObj.Free;
end;
end;
Большая часть кода предназначена для выполнения тех же рутинных задач, что и в модели с одним потребителем, представленной в листинге 12.14, за исключением того, что на этот раз необходимо заботиться о нескольких потребителях. Полный код подпрограммы находится в файлах TstNCpy.dpr и TstNCpyu.pas на Web-сайте издательства, в разделе материалов.
Поиск различий между двумя файлами
Рассмотрим следующую задачу. Имеются две версии исходного файла, одна из которых - более поздняя, содержащая ряд изменений. Как выяснить различия между этими двумя файлами? Какие строки были добавлены, а какие удалены? Какие строки изменились?
Существует множество программ, выполняющих подобные функции. В их числе и программа diff, которую можно считать прародительницей всех программ сравнения файлов. Пакет Microsoft Windows SDK содержит программу, названную WinDiff. Программа Visual SourceSafe, поставляемая компанией Microsoft, также предоставляет функцию, которая позволяет выбрать две версии файла, хранящиеся в базе данных, и просмотреть различия между ними.
-------
Этот раздел адресован только тем программистам, которые работают в 32-разрядной среде. Рассмотренный здесь алгоритм является рекурсивным и интенсивно использует программный стек. Delphi1 не поддерживает достаточно большой стек, чтобы с его помощью можно было реализовать этот алгоритм даже для сравнительно умеренных по размеров файлов.
-------
Потратим несколько минут, и попытаемся определить требуемый для выполнения этой задачи алгоритм. Раньше я уже пытался сделать это, что оказалось достаточно трудно. Кое-что можно упростить сразу: изменение строки можно считать удалением старой строки и вставкой новой. Мы не будем углубляться в проблемы семантики, пытаясь выяснить, насколько сильно изменилась строка. Мы всего лишь будем рассматривать все изменения в текстовом файле как набор удаленных строк и набор вставленных новых строк.
Вычисление LCS двух строк
Требуемый нам алгоритм известен под названием алгоритма определения наиболее длинной общей подпоследовательности (longest common subsequence - LCS). Вначале мы рассмотрим, как он работает применительно к строкам, а затем расширим приобретенные представления на текстовые файлы.
Уверен, что все мы играли с детскими головоломками, в которых нужно было преобразовать одно слово в другое, изменяя по одной букве. Все промежуточные варианты должны были быть также осмысленными словами. Так, преобразуя слово CAT в слово DOG, можно было бы выполнить следующие преобразования: CAT, COT, COG, DOG.
Смысл этих игр со словами заключается в простом удалении на каждом шаге одной буквы и вставке новой. Если бы не ограничения, накладываемые правилами игры, можно было бы наверняка преобразовать одно слово в другое, просто удалив все старые символы и вставив вместо них новые. Такой метод решения задачи можно сравнить с применением кувалды, нам же весьма желательно найти несколько более тонкий подход.
Предположим, что наша цель заключается в отыскании наименьшего количества изменений, требуемых для преобразования одного слова в другое. Для примера преобразуем слово BEGIN в слово FINISH. Мы видим, что нужно удалить буквы В, Е и G, а затем вставить букву F перед оставшимися буквами и буквы I, S и H после них. Как же реализовать эти действия в виде алгоритма?
Один из возможных способов предполагает просмотр подпоследовательностей букв каждого слова и выяснение наличия в них общих последовательностей. Подпоследовательность (subsequence) строки образуется за счет удаления из нее одного или более символов. Оставшиеся символы не должны переставляться. Например, четырехбуквенными подпоследовательностями для строки BEGIN являются EGIN, BGIN, BEGIN, BEIN и BEGI. Как видите, они образуются путем поочередного отбрасывания одного из символов. Трехбуквенными подпоследовательностями являются BEG, BEI, BEN, BGI, BGN, BIN, EGI, EGN, EIN и GIN. Для данного слова существует 10 двухбуквенных подпоследовательностей и пять одно-буквенных. Таким образом, для пятибуквенного слова существует всего 30 возможных подпоследовательностей, а в общем случае можно было бы показать, что для n-буквенной последовательности существует около 2(^n^) подпоследовательностей. Пока что примите это утверждение на веру.
Алгоритм с применением "грубой силы", если его можно так назвать, заключается в просмотре двух слов BEGIN и FINISH и просмотре их пятибуквенных подпоследовательностей на предмет наличия каких-либо совпадений. Такие совпадения отсутствуют, поэтому для каждого слова то же самое нужно сделать, используя четырехбуквенные подпоследовательности. Как и в предыдущем случае, ни одна из подпоследовательностей не совпадает, поэтому мы переходим к рассмотрению трехбуквенных последовательностей. Результат снова отрицателен, поэтому мы переходим к сравнению двухбуквенных подпоследовательностей. Самой длинной общей подпоследовательностью этих двух слов является IN. Исходя из этого, можно определить, какие буквы необходимо удалить, а какие вставить.
Для коротких слов, подобных приведенному примеру, описанный подход не так уж плох. Но представим, что требуется просмотреть все подпоследовательности 100-символьной строки. Как уже упоминалось, их количество составляет 2(^100^). Алгоритм с применением "грубой силы" является экспоненциальным. Количество выполняемых операций пропорционально O(2(^n^)). Даже для строк средней длины поле поиска увеличивается чрезвычайно быстро. А это влечет за собой радикальное увеличение времени, требуемого для отыскания решения. Чтобы сказанное было нагляднее, представим следующую ситуацию: предположим, что можно генерировать около биллиона подпоследовательностей в секунду.(т.е. 2(^40^)= 1 099 511 627 776, или тысячу подпоследовательностей за один такт работы процессора ПК, тактовая частота которого равна 1 ГГц). Год содержит около 2(^25^) секунд. Следовательно, для генерации всего набора подпоследовательностей для 100-символьного слова потребовалось бы 2(^35^) (34 359 738 368) лет - 11-значное число. А теперь вспомните, что 100-символьная строка - всего лишь простенький пример того, что необходимо сделать: например, найти различие между двумя вариантами 600-строчного исходного файла.
Однако идея применения подпоследовательностей обладает своими достоинствами. Просто нужно подойти к ней с другой стороны. Вместо перечисления и сравнения всех подпоследовательностей в двух словах посмотрим, нельзя ли применить пошаговый подход.
Для начала предположим, что нам удалось найти наиболее длинную общую подпоследовательность двух слов (далее для ее обозначения мы будем использовать аббревиатуру "LCS"). В этом случае можно было бы соединить линиями буквы в LCS первого слова с буквами LCS второго слова. Эти линии не будут пересекаться. (Это обусловлено тем, что подпоследовательности определены так, что перестановки букв не допускаются. Поэтому буквы в LCS в обоих словах будут располагаться в одинаковом порядке.) LCS для слов "banana" и "abracadabra" (т.е. b, а, а, а) и линии, соединяющие совпадающие в них буквы, показаны на рис. 12.1. Обратите внимание, что для этой пары слов существует несколько возможных LCS. На рисунке, показана лишь первая из них (занимающая самую левую позицию).

