Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по алгоритмам.doc
Скачиваний:
3
Добавлен:
05.11.2018
Размер:
264.7 Кб
Скачать

Глава 1. Разработка эффективных алгоритмов

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

1.1. Алгоритмы и их сложности

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

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

Именно асимптотическая сложность алгоритма определяет в итоге размер задач, которые можно решить этим алгоритмом. Если алгоритм обрабатывает входы размера n за время cn2, где c  некоторая постоянная, то говорят, что временная сложность этого алгоритма есть O(n2) (читается «порядка n2»). Точнее, говорят, что неотрицательная функция g(n) есть O(f(n)), если существует такая постоянная c, что g(n) cf(n) для всех неотрицательных значений n, кроме конечного (возможно пустого) множества.

Может показаться, что колоссальный рост скорости вычислений, вызванный появлением современных ЭВМ, уменьшит значение эффективных алгоритмов. Однако происходит в точности противоположное. Так как вычислительные машины работают всё быстрее и быстрее, и пользователь может решать всё бóльшие задачи, именно сложность алгоритма определяет то увеличение размера задачи, которое можно достичь с увеличением скорости машины.

Допустим, что имеется пять алгоритмов A1, …, A5 со следующими временными сложностями:

Алгоритм Временная сложность

A1 n

A2 nlogn

A3 n2

A4 n3

A5 2n

Здесь временная сложность  это число единиц времени, требуемого для обработки входа размера n. Пусть единицей времени будет одна миллисекунда. Тогда алгоритм A1 может обработать за одну секунду вход размера 1000, в то время как A5  вход размера не более 9. В таблице 1 приведены размеры задач, которые можно решить за указанные промежутки времени каждым алгоритмом.

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

Таблица 1. Границы размеров задач в зависимости от сложности алгоритма

Алгоритм

Временная сложность

Максимальны размер задачи

1 секунда

1 минута

1 час

A1

n

1000

6104

3,6106

A2

nlogn

140

4893

2105

A3

n2

31

244

1897

A4

n3

10

39

153

A5

2n

9

15

21

Таблица 2. Эффект стократного увеличения скорости счёта

Алгоритм

Временная сложность

Максимальны размер задачи

до ускорения

после ускорения

A1

n

s1

100s1

A2

nlogn

s2

100s2 для больших s

A3

n2

s3

10s3

A4

n3

s4

4.64s4

A5

2n

s5

s5+6.64

Видно, что для алгоритма A5 стократное увеличение скорости увеличивает размер задачи, которую можно решить за то же время, только на 6, тогда как для алгоритма A3 размер задачи удесятеряется.

Вместо эффекта увеличения скорости рассмотрим эффект применения более действенного алгоритма. Вернёмся к таблице 1. Если в качестве основы сравнения взять 1 минуту, то, заменяя алгоритм A4 алгоритмом A3, можно решить задачу в 6 раз бóльшую, а, заменяя A4 на A2, можно решить задачу, бóльшую в 125 раз. Эти результаты производят гораздо бóльшее впечатление, чем пятикратное улучшение, достигаемое за счёт 100-кратного увеличения скорости. Если в качестве основы для сравнения взять один час, то различие оказывается еще более значительным. Отсюда можно заключить, что асимптотическая сложность алгоритма служит важной мерой качества алгоритма, причем такой мерой, которая обещает стать еще важнее при последующем увеличении скорости вычислений.

Но необходимо учитывать не только порядок n, но и константу c, которая тоже может влиять на качество алгоритма. Предположим, что временные сложности алгоритмов A1, …, A5 в действительности равны соответственно 1000n, 100nlogn, 10n2, n3 и 2n. Тогда A5 будет наилучшим для задач размера 2n9, A3  для 10n58, A2  для 59n1024, A1  для n>1024.

Пример 1.1. Составить на языке C программу расчета значения полинома по заданным степени полинома, его коэффициентам и величине x.

Требуется вычислить значение:

Pn(x) = anxn + an-1xn-1 + … + a1x + a0 (1.1)

по заданным: a0, a1, …, an; степени полинома n и значению x. Попробуем написать программу для этой процедуры в виде функции на языке C, не вдаваясь в подробности ввода указанной информации.

Сначала составим программу, пользуясь прямым алгоритмом, не учитывая особенности машинных программ.

// Программа 1.1

double Polinom_Calculation(double Array[], unsigned int Order, double Value)

/* Функция расчета значения полинома по заданным коэффициентам, степени

полинома и значению переменной x. */

{

double p=Array[0];

unsigned int index;

/* Array массив коэффициентов полинома a0, a1,…, an; Order степень полинома;