Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СТА (лекции+лабы) / СТА Лекция 5.docx
Скачиваний:
85
Добавлен:
16.03.2016
Размер:
67.45 Кб
Скачать

Асимптотическая оценка вычислительной сложности

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

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

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

В общем виде, время выполнения любого алгоритма можно представить следующим образом:

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

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

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

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

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

O-оценка является подмножеством -оценки, ограничивающим функцию анализируемого алгоритма только сверху. Иначе говоря, О-оценка асимптотически описывает худший случай для анализируемого алгоритма. Такая оценка используется при анализе наиболее часто. Существенно реже используется-оценка, ограничивающая функцию анализируемого алгоритма снизу (лучший случай).

Среди типично встречающихся асимптотических оценок вычислительной сложности алгоритмов распространенными являются следующие функции:

  • линейная O(N) (время пропорционально увеличению данных);

  • квадратичная O(N2);

  • полиномиальная сложность O(NM), в частности, кубическая O(N3);

  • экспоненциальная сложность O(2N);

  • факториальная сложность O(N!);

  • логаримфическая сложность O(log(N)) (подразумевают логарифм по основанию 2);

  • линейно-логарифмическая сложность O(N * log(N)) ;

  • константная вычислительная сложность O(1) (время не зависит от количества данных).

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

Вычислительную сложность разрабатываемых алгоритмов следует максимально ограничивать, насколько это является возможным. Соотношение между данными оценками можно ощутить, представив количество выполненных инструкций на конкретных примерах, скажем при N=5, 10, 25, 100 и 500 (положим, что константные коэффициенты одинаковы для упрощения понимания). При таком объеме данных получим следующие показатели:

Оценка

N=5

N=10

N=25

N=100

N=500

1

1

1

1

1

3

4

5

7

9

5

10

25

100

500

15

40

125

700

4500

25

100

625

10 000

250 000

125

1 000

15 625

1 000 000

125 000 000

32

1 024

33 554 432

очень много

очень много

120

3 628 800

очень много

очень много

очень много

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

Если программа относится к научно-исследовательским, предельно допустимой сложностью является полиномиальная, в частности, кубическая . Алгоритмы с более высокой вычислительной сложностью имеют применение только для малых значений N, в противном случае задачи не имеют компьютерного решения с достижимыми вычислительными затратами.

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

Поверхностная оценка вычислительной сложности

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

Ниже приведено несколько подсказок для поверхностной оценки вычислительной сложности “”на глаз” без строгого аналитического подхода.

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

  1. Вычислительная сложность последовательности инструкций равна максимальной сложности входящих в нее инструкций.

  1. Если нет никаких специальных сведений о вероятности срабатывания условных переходов, то все возможные условные переходы, в том числе неявные (опущенные ветки else, default), следует считать равновероятными. Сложность каждого блока инструкций оценивается отдельно, а затем выбирается максимальная из них, что и становится результатом оценки для условной конструкции в целом.

  1. Если инструкция находится в теле цикла, количество итераций которого зависит от N, то вычислительная сложность инструкции умножается на линейную функцию.

  1. Вычислительная сложность двух циклов, зависящих от N, вложенных друг в друга, скорее всего описывается квадратичной функцией. Соответственно, вложенность из 3 уровней соответствует кубической сложности.

  1. Если алгоритм разбивает набор входных данных на части, а затем обрабатывает их отдельно тем же самым алгоритмом рекурсивно - вычислительная сложность является логарифмической.

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

int factorial ( int _x )

{

if ( _x < 1 )

return 0;

else if ( _x == 1 )

return 1;

else

return _x * factorial( _x - 1 );

}

Первые два разветвления основного условия являются элементарными инструкциями, их вычислительная сложность оценивается как O(1). Что же касается последней ветки, сложность описывается, так называемым, рекуррентным соотношением:

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

Соседние файлы в папке СТА (лекции+лабы)