О способах оценки сложности (эффективности) алгоритмов




О задачах поиска и сортировки для массивов и файлов

 

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

type Книга = record

Автор: string;

Название: string;

ГодИздания: word;

Тема: (Математика, Биология, ОБЖ);

Стеллаж: word; {номер стеллажа, где эта книга находится в библиотеке}

Количество: word; {штук в библиотеке}

end;

Здесь можно было бы “более детально” организовать почти все из име­ющихся полей данных, например, тип поля Автор сделать таким:

record

Фамилия, Имя, Отчество: string [20];

end;

однако ни здесь, ни ниже мы не будем заниматься подобной детализа­цией.


Из таких записей на Паскале может быть создана база данных, на­пример, для описанного в ыш е типа Книга:

array [1..N] of Книга;

для некоторой (достаточно большой) константы N, или

file of Книга;

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

Когда имеется много данных описанного типа, практически всегда удобно работать с отсортированными данными. Отметим, что для раз­ных задач может потребоваться сортировка по разным полям. Например, для массива (или файла) данных – элементов типа Книга могут потре­боваться такие виды сортировки:

– по отношению «меньше» - по одному из полей ГодИздания, Стеллаж или Количество,

– алфавитное - по одному из полей Автор или Название

В любом случае можно считать, что имеется некоторое отношение порядка, и именно по этому отношению производятся сортировка и поиск в отсортированном массиве (файле) данных.

Всюду ниже будем для упрощения записи сортировать массивы (фай­лы) по отношению “меньше” для типа word, и поиск в уже отсортирован­ном массиве проводить также для этого отношения. Таким образом, «не ограничивая общности рассуждений», в дальнейшем можно будет рабо­тать только с данными следующего типа:

type Элемент = record

Ключ: word;

Информация: ТипИнформации;

end;

Здесь Ключ – поле, по которому ведётся сортировка (поиск), а тип ТипИнформации – абстракция для всех остальных полей типа. Напри­мер, для типа Книга вместо поля Ключ может быть применено по­ле ГодИздания, а поле Информация в этом случае обозначает совокуп­ность полей Автор, Название, Тема, Стеллаж и Количество. Подробнее ТипИнформации конкретизировать не будем – приведённые далее про­граммы работают с различными типами данных. Иногда будем назы­вать элемент массива по его ключу (т. е. если Ключ=51, то будем говорить “элемент 51”).

Выше были описаны четыре возможные задачи – задачи сортиров­ки и поиска для массивов и файлов данных. При этом имеются в ви­ду последовательные файлы (что подчёркивается употреблением записи file of): действительно, задачи сортировки (поиска) для файлов пря­мого доступа аналогичны соответствующим задачам для массивов. За­дача поиска для последовательных файлов тривиальна: никаких других алгоритмов, кроме последовательного просмотра всех элементов файла, придумать невозможно. Таким образом, из четырёх задач остаются три. Ниже (т.е. в данной части 1) будут рассмотрены две из них – обе за­дачи для массивов, они значительно проще задачи сортировки файлов, которая будет рассмотрена в одной из следующих частей.

Далее в обеих задачах про массивы мы не будем использовать ни­каких дополнительных структур данных (например, вспомогательных массивов), кроме, возможно, нескольких переменных простых типов - word, boolean и т.п., а также одной переменной описанного выше типа Элемент (эта переменная будет использоваться для обмена двух элемен­тов массива). При этом, однако, алгоритмы сортировки вовсе не услож­няются - т.е. они не сложнее тех алгоритмов, которые можно было бы создать, применяя вспомогательные массивы. А сложность алгорит­мов - т.е. различные способы оценки их эффективности - будет рассмот­рена в следующем разделе.



О способах оценки сложности (эффективности) алгоритмов

 

Определения сложности или эффективности алгоритмов, приводимые в разных монографиях ([5, 9, 15] и др.), весьма отличаются друг от друга, поэтому имеет смысл говорить не о строгом определении эффективности, а о различных подходах к её определению. А при необходимости в каждом конкретном случае один из подходов можно будет “развить” до строгого математического определения. Ниже приведены альтернативные варианты таких подходов. Альтернативы обозначены буквами (A,B,C,D) с нижними индексами, т.е. например, можно создать строгое определение на основе приведённых ниже альтернатив A2, B1,2, C2 и D1. Но в любом случае эффективность - некоторая функция, зависящая от размерности задачи (эта функция обычно является возрастающей)[1] Пока будем считать размерность зафиксированной.

Во-первых, исследование эффективности можно проводить:

(A1) аналитическое;

(A2) статистическое, на основе специальных тестов. Большинство дальнейших примеров этого раздела основаны на статисти­ческой оценке (при этом примеры более наглядны, кроме того, аналити­ческое исследование не всегда возможно). В следующих разделах, на­оборот, будем оценивать эффективность аналитически, однако несколь­ко различных программ для статистических оценок читателям всё же полезно написать самостоятельно.

Во-вторых, измерять эффективность можно:

(B1) по времени;

(B2) по объёму занимаемой памяти.

По-видимому, временная оценка эффективности алгоритмов на практике требуется значительно чаще, поэтому ниже в настоящем пособии будем рассматривать именно её.

В [5] можно найти таблицу зависимости максимальной размерности задачи, выполняемой за единицу времени (секунду, минуту, час), от вре­менной сложности алгоритма (n, n2, n [2] , 2n или nlogn), а также таб­лицу возможного увеличения максимальной размерности при использо­вании более быстрого вычислительного устройства. При желании такие таблицы легко построить самостоятельно.

Имеются различные подхо ды к «единицам измерения» временной эф­фективности. В первом подходе для этого выбираются «обычные» временные еди­ницы (например, миллисекунды), и для программы, соответствую­щей рассматриваемому алгоритму, измеряется время работы. Недостатки такого подхода следующие. Во-первых, имеется очень большая зависимость от качественного программирования, т.е. от эффективности реализации алгоритма. Во-вторых, даже при опти­мальных (или близких к ней) реализациях эффективность зависит от применяемых компьютеров: сравнительные результаты работы двух разных алгоритмов могут сильно отличаться на компьютерах различных типов.

(B1,2) Попробуем избавиться от всех отмеченных недостатков предыду­щего подхода, выбирая характерные для нескольких однотипных алгоритмов операции и подсчитывается число применений каждой из них (ср. измерение длины удава «в попугаях»)[3]. Различные спо­собы подсчёта числа применений некоторой операции будут описа­ны ниже.

И у этого подхода легко заметить недостаток: где гарантия, что на­ми действительно были выбраны «самые важные» операции? Ведь во время работы алгоритма выполняются и другие операции, и, может быть, время выполнения остальных операций сравнимо со временем выполнения выбранных.

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

Для любого из отмеченных способов оценки времени можно измерять:

(C1) усреднённую;

(C2) худшую

эффективность для нескольких измерений. (Далее будем гово­рить «эффективность в среднем» и «в худшем».) При этом могут быть получены разные сравнительные результаты, один из подобных приме­ров имеется и в настоящем пособии - см. раздел 5.

Какие же случаи нужно рассматривать для выбора худшего из них или усреднения получаемой эффективности? Здесь тоже имеются по крайней мере два подхода:

(D1) случайным образом генерируются несколько различных вариантов входных данных;

(D2) Рассматриваются (или с помощью некоторой комбинаторной про­цедуры генерируются) все принципиально различные возможные входы. [4] Однако очевидно, что из-за «комбинаторного взрыва» та­кой способ можно применять только для очень малых размерностей задачи.

Сравнительная эффективность каких-либо двух алгоритмов часто зависит и от размерности задачи, которую мы пока считали зафикси­рованной. Так, при достаточно малых размерностях обычно лучше ра­ботают простые алгоритмы, а при больших размерностях – более сложные, не сразу очевидные алгоритмы. Иллюстрирующие примеры можно легко получить на осно­ве алгоритмов из настоящего пособия[5]. Вследствие этого факта часто говорят о предельной эффективности алгоритма как некоторой функции f (n) - в случае, если предел

существует и отличен от 0 и (здесь n - размерность, а Q(n) - эф­фективность рассматриваемого алгоритма). Или в других обозначениях: Q(n)=O(f (n)).

В заключение отметим, что далее в настоящем пособии будет приме­няться то определение эффективности алгоритмов, которое может быть записано на основе следующих альтернатив: A 1, B 1,2, C2 и D2.




Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-08-20 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: