
3.2. Сложность алгоритма. Классификация алгоритмов по сложности
Размер задачи r(n) оценивает величину памяти, необходимой для представления исходных данных задачи в ЭВМ.
Однако, возможность представления условия задачи в памяти ЭВМ не гарантирует её практического решения. Во-первых, как правило, нужна дополнительная память для хранения промежуточных и окончательных результатов счёта.
Во–вторых, время решения может оказаться недопустимо большим. И первое условие и второе зависят от алгоритмов, применяемых при решении задачи.
Размер дополнительной памяти, как правило, определить несложно. Рассмотрим время счёта. В процессе реализации расчётного алгоритма, выбранного для решения задачи, необходимо выполнять определённое число элементарных арифметических, логических и других операций – сложения, вычитания, умножения, деления, возведения в степень, сравнения и т. д.
Пусть в процессе решения задачи при помощи некоторого алгоритма выполняются элементарные операции с номерами 1, 2, ..., k. Среднее время выполнения этих операций на используемой ЭВМ обозначим через t1, ... , tk , число операций, зависящее от применяемого алгоритма решения – через g1(n), ... , gk(n).
Обозначим полное время счёта алгоритма через Т(n). Достаточно точно его можно выразить в виде суммы: Т(n) = Т1(n) + Т2(n)+...+Тk(n), где Т1(n), Т2(n), ... ,Тk(n) – общее время выполнения каждой из операций 1,2,...,k. Выражая все величины Тi(n) через ti и gi(n), получим:
Рассмотрим зависимость скорости роста общего времени счета алгоритма Т(n) от чисел выполняемых операций g1(n) – gk(n).
Теорема 3.1 о скорости роста времени счета алгоритма. Пусть при выполнении алгоритма используются элементарные операции 1, 2, ... , k. У операции с номером s (1 s k) число выполняемых операций gs(n) имеет устойчивую скорость роста fs(n). У остальных операций i (1 i k , i s) числа выполняемых действий gi(n) ограничены сверху функциями со скоростями роста, меньшими fs(n).
Тогда при n→∞ скорость роста общего времени счёта алгоритма Т(n) будет устойчивой и равна скорости роста fs(n) независимо от среднего времени выполнения t1 , ... , tk элементарных операций f1(n), ... , fk(n).
Доказательство. Представим общее время счета алгоритма Т(n) в следующем виде:
У операции s для числа выполняемых действий gs(n) по определению одинаковой скорости роста с fs(n) существуют такие значения n0sN, С1s>0, С2s >0 , что при n n0s выполняется условие: С1s fs (n) ≤ gs (n) ≤ С2s fs (n).
Рассмотрим остальные операции i (1 i k , i s). Очевидно, gi (n) 0. По определению ограниченности сверху для любой операции i существуют такие значения n0i N, С2i >0 , что при n n0i выполняется условие: gi (n) ≤ С2i fi (n).
Принимая:
зависимость всех пар функций gi(n), fi(n) при n n0 можно представить в унифицированном виде: gi (n) ≤ С2 fi (n) , а для общего времени счета – записать неравенство:
Поскольку все скорости роста fi (n) при i s меньше скорости роста fs (n), то по определению для константы С=1 всегда найдется такое значение параметра n0i N, что при n n0i справедливо: fi(n) ≤ fs(n). Принимая в качестве n0 = max(n0, n01, … , nik), получим, что при n n0 правое неравенство может быть представлено в виде:
Вводя новые константы:
получим,
что при
n
n0
выполняется
соотношение, доказывающее справедливость
теоремы:
С1 fs (n) ≤ T(n) ≤ С2 fs (n),
т.е. одинаковую скорость роста у общего времени счета T(n) и у максимальной из скоростей роста числа элементарных операций алгоритма, что и требовалось доказать.
При анализе элементарных арифметических операций алгоритма и подсчете их числа необходимо учитывать тип величин, с которыми выполняются данные действия. Представление целых и вещественных чисел в памяти ЭВМ различно. Операции с целыми числами в процессоре выполняет арифметико-логическое устройство, а операции с вещественными числами - сопроцессор для чисел с плавающей запятой.
Замечания. 1. Одинаковая скорость роста числа выполняемых операций может быть у нескольких элементарных операций. Справедливость доказанного утверждения при этом сохраняется.
2. Если у максимально растущих чисел операций gi(n) скорость роста не является устойчивой, а известна лишь верхняя ее оценка, то справедлив следующий вариант теоремы 3.1.
Теорема 3.2 о верхней оценке скорости роста времени счета алгоритма. Пусть при выполнении алгоритма используются элементарные операции 1, ... , k. Числа этих операций g1(n), … , gk(n) ограничены сверху скоростью роста функции f(n).
Тогда при n→∞ скорость роста общего времени счёта алгоритма Т(n) также будет ограничена сверху скоростью роста функции f(n).
Доказательство Теоремы 3.2 можно выполнить по аналогии с доказательством Теоремы 3.1.
Следствие. Скорость роста общего времени счёта алгоритма Т(n) определяется скоростями роста f1(n), ... , fk(n) чисел элементарных операций g1(n), ... , gk(n) либо их верхними границами.
Скорость роста общего времени счёта алгоритма Т(n) называют сложностью алгоритма и обозначают через f(n).
В зависимости от сложности все комбинаторные алгоритмы делятся на несколько основных групп.
Полиномиальными называются алгоритмы, сложность которых ограничена полиномом.
Примеры 1: а) f(n) = n, б) f(n) = n 2, в) f(n) = nlogn < n 2.
Экспоненциальными называются комбинаторные алгоритмы, сложность которых в пределе при бесконечном возрастании n превышает полином любой степени.
Примеры 2: а) f(n) = е n, б) f(n) = n n, в) f(n) = n!, г) f(n) = 2 n.
Субэкспоненциальными называются алгоритмы, сложность которых в пределе при бесконечном возрастании n превышает полином любой степени, но меньше 2 nδ при любом δ>0.
Пример 3. f(n) = n logn.
Рассмотрим примеры расчета сложности алгоритмов.
Пример 4. Задача: упорядочить по убыванию элементов линейный числовой массив А[n]i.
Предлагаемый алгоритм решения: внутренняя (без использования дополнительной памяти) сортировка с выбором. В данном алгоритме выполняется n –1 шаг. На первом определяется минимальный из n элементов a1, a2, … , an и путем обмена с a1 помещается на первое место. На втором определяется минимальный из n–1 элементов a2, … , an и путем обмена с a2 помещается на второе место и т.д.
Необходимо определить сложность алгоритма как функцию характерного числа задачи, которым является длина массива n.
Решение. В алгоритме используются две основные операции – 1) сравнение величин элементов массива и 2) обмены пар элементов местами. Определим в зависимости от параметра задачи n числа выполняемых основных операций. Обозначим их, соответственно, g1(n) и g2(n). Так как всего выполняется n – 1 шаг, на каждом один обмен, то g2(n) = n – 1. На первом шаге выполняется (n - 1) сравнение, на втором – (n – 2), … , на последнем (n – 1) –том – одно сравнение. g1(n) определяем как сумму арифметической прогрессии:
g1(n) = (n – 1)+ (n – 2)+…+1 = n(n - 1)/2.
Так как для найденных функций выполняются следующие предельные оценки:
то по определению g1(n) имеет квадратичную полиномиальную скорость роста, а g2(n) – линейную.
Ответ: поскольку у операции сравнения 1 устойчивая квадратичная полиномиальная скорость роста, а у операции обмена 2 – устойчивая линейная скорость, то по Теореме 3.1 скорость роста общего времени счёта (сложность) алгоритма будет устойчивой и равна максимальной из скоростей роста чисел элементарных операций, т.е. будет квадратичной полиномиальной.
Пример. 5. Необходимо определить сложность алгоритма, заключающегося в полном переборе обходов в задаче коммивояжера (ЗК) для n населенных пунктов {vn }={v1, v2, ... , vn}(пример 5 п.3.1).
Решение. На каждом из (n–1)!/2 возможных обходов при полном переборе требуется выполнить следующие операции:
1) сформировать сам обход Оn={vi1, vi2, ... , vin} ,
2) вычислить длину обхода {vi1, vi2, ... , vin} L(Оn):
3) сравнить длину обхода L(Оn) с текущим значением минимума Lmin и при необходимости – скорректировать Lmin.
Поскольку формирование обхода Оn требует n элементарных операций, вычисление длины – также n сложений, то числа выполняемых операций g1(n) (формирование обхода), g2(n) (сложение), g3(n) (сравнение длин обхода) будут следующими:
g1(n)= n! /2, g2(n)= n! /2, g3(n)= (n–1)!/2 .
Для найденных функций выполняются следующие предельные оценки:
По Теореме 3.1 сложность алгоритма будет устойчивой и равна максимальной из скоростей роста чисел элементарных операций, т.е. будет факториальной по n.
Ответ: Сложность алгоритма относится к экспоненциальному типу.
Сравнительные значения величин функций для трёх рассмотренных типов скоростей роста при значениях характерного параметра n = 10, 100, 1000 приведены в таблице.
NN Вид алгоритма Функция n = 10 n =100 n = 1000
n 1 10 102 103
nlogn 10 2102 3103
1. Полиномиальный n 2 102 104 106
n 3 103 106 109
n10 1010 1020 1030
2. Субэкспоненциальный nlnn 200,7 1,621013 5,291029
еn 0.22105 0.271043 0.7810208
3. Экспоненциальный 2n 0.1104 0.121031 0.110301
n! 0.36 107 10158 4102567
nn 1010 10200 103000