Лекция 9. Сложность вычислений с помощью алгоритмов

ТЕОРИЯ АЛГОРИТМОВ

 

Понятие о сложности

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

Следовательно, принципиальная алгоритмическая разрешимость ещё не означает практическую реализуемость. Рассмотрим некоторые характеристики вычислений.

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

Различают сложность описания алгоритма и исходных данных, и сложность применения алгоритма к исходным данным.

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

Введем следующее определение. Говорят, что неотрицательная функция g(n) есть 0(f(n)) если существует такая постоянная c, что g(n)≤cf(n) для всех, кроме конечного (возможно пустого) множества значений n, {1, 2, 3,…}. В этом случае записываем: g(n)=0(f(n)).

Сложность исходных данных понимается как длина (размер) их записи. Что такое размер входа? Всё зависит от того, что является входом. Размером входа, в общем случае, считают число символов, с помощью которых записан вход.

Пусть входом является целое число. Считаем, что число представлено в системе счисления с некоторым фиксированным основанием. В этих системах число символов, необходимых для представления целого числа n равно ]logAn[, где – основание системы, а ]х[ обозначает наименьшее целое q, такое, что . Известно, что , здесь log2B является константой при фиксированном В. Таким образом, число символов, необходимых для представления целого числа n есть 0(log2n).

Рассмотрим второй пример. Пусть требуется перемножить квадратные n×n матрицы А и В. Ясно, что подходящей мерой сложности исходных данных будет число пропорциональное l= n2, имея в виду, что для запоминания отдельного числа (элемента матрицы) выделен определенный объем памяти.

Во многих задачах входом является граф. Граф G=(V,X) можно, например, задать его матрицей смежностей АG=(aij) размера , где aij=1, если ребро и aij=0 в противном случае. Ясно, что максимальное число возможных ребер равно Однако многие графы являются разреженными, т. е. число их ребер много меньше М. В этом случае лучше задать граф перечислением его ребер, с помощью списков смежностей.

При задании списков смежностей для каждой вершины , выписывается множество вершин, смежных с v,

 

 

Размер этого представления зависит от суммы длин списков. Каждое ребро вносит вершину в два списка, поэтому сумма длин списков содержит элементов. Но для различения вершин, как правило, вводятся числовые индексы. Так как имеется вершин, то для индексов потребуется двоичных или десятичных разрядов. Следовательно, при таком представлении графа G=(V,X) потребуется символов.

Более экономной записью информации в ячейках памяти ЭВМ (выделенного для одного числа) можно добиться, что для задания графа G=(V,X) требуется ячеек памяти.

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

Кроме того, сложность вычислений зависит от способа формулировки задачи. В качестве примера рассмотрим следующую задачу. Требуется узнать, является ли натуральное число n простым или составным. Чтобы анализировать сложность задачи надо выяснить, как задано число n. Если n задано как произведение своих простых делителей: , то задачи нет вообще; если n задано в унарной форме, т.е. n=11…1, то сложность записи равна (числу единиц), а сложность задачи, как можно показать, равна = (если использовать известный способ решения этой задачи – решето Эратосфена, состоящий в том, что число N последовательно делят на простые числа p1,p2,…, . Если ни на одно из этих чисел N не делится, то оно простое, иначе составное). Если число n записано в десятичной форме, то сложность записи числа (длина записи) равна l=log10n, а сложность решения в этом случае равна =2l/2, т.е. сложность решения представляет собой экспоненту от длины записи числа n.

Следует обратить внимание на то обстоятельство, что одной и той же задаче могут соответствовать разные языки, представляющие условия или входные данные задачи. Это связано со способами кодировки данных. Из всех языков представляющих исходную задачу, выбирается «разумный язык» или разумный способ кодировки её условий. Таким образом, каждой задаче соответствует “разумный язык” её представляющий.

 

Временная сложность вычислений (алгоритма)

 

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

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

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

Кроме меры сложности в наихудшем случае вводят временную сложность алгоритма А в среднем на входе размера n:

здесь p(ai) – вероятность появления задачи ai; µ(A(ai)) - число операций, затрачиваемых алгоритмом на решение индивидуальной задачи ai; Рn– множество рассматриваемых задач размера n, Рn={ ai}

При изучении сложности алгоритма, в основном интересуются его поведением при применении его к очень большим входам. Различие между сложностями 10n3и 9n3считаются несущественными, более важен показатель степени, а не коэффициент. Сложность алгоритма оценивается асимптотической сложностью, т.е. порядком роста числа операций при неограниченном росте размера входа. Например, если вход размера n обрабатывается за время cn2, где с – некоторая постоянная, то сложность этого алгоритма есть 0(n2), т.е. постоянная с не содержится в оценке. Для практических задач величина этого коэффициента может быть важна, если они различаются существенно. Если время работы алгоритмов А1 и А2 пропорционально, например, 1010n2и 9n2соответственно, то практическое использование алгоритма А1 проблематично.

Для решения выбранной задачи иногда можно использовать различные алгоритмы.

 

Под временной сложностью задачи понимается временная сложность наилучшего алгоритма, известного для ее решения.

 

Выясним как изменяется эффективность различных алгоритмов с ростом быстродействия ЭВМ на которых реализуются эти алгоритмы.

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

 

Из этой таблицы видно, что увеличение времени решения задачи, например с 1-ой секунды до одного часа позволяет для алгоритма А1 увеличить размер решаемой задачи в 3600 раз, а для алгоритма А5 только в 2,33 раза.

Предположим, что быстродействие ЭВМ возросло в 10 раз. В нижеследующей таблице показано, как при этом возрастут размеры входов [29].

 

Из таблицы видно, что увеличение быстродействия в 10 раз даёт возможность для алгоритма А1 увеличить размер входа в 10 раз, а для алгоритма А5 увеличение размера входа практически не произошло. Таким образом, чем большую временную сложность имеет алгоритм, тем меньшее улучшение даёт увеличение быстродействия.

 

Полиномиальные алгоритмы и задачи. Класс Р

 

Считается, что алгоритм – полиномиальный или имеет полиномиальную временную сложность, если существует полином p(x) такой, что на любом входном слове длины n алгоритм завершает вычисления после выполнения не более чем p(n) операций.

Ясно, что, алгоритмы А1 и А2, временные сложности которых равны, например, будет считаться полиномиальными, ибо их сложности ограничены полиномами, т.е. имеют порядок не выше, чем и соответственно.

Говорят, что задача разрешима за полиномиальное время или полиномиально разрешима, если для неё существует полиномиальный алгоритм. При этом считается, что задача является «хорошей».

Множество всех задач, для каждой из которых существует полиномиальный алгоритм, называется классом Р.

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

В класс Р кроме линейных задач попадают, например, следующие задачи.

Умножение целых чисел. Школьный метод умножения 2-х n-разрядных чисел имеет временную сложность порядка . Существует алгоритм Шёнхаге-Штрассена умножения чисел (заданных в двоичной системе счисления) с меньшей сложностью, именно со сложностью порядка .

Умножение матриц. Обычный метод имеет порядок сложности 0(n3). Существует более быстрый алгоритм умножения матриц - алгоритм Штрассена [2] который имеет сложность порядка .

Найти кратчайший путь на графе состоящем из n вершин и m рёбер. Сложность алгоритма 0(mn) .

Быстрое преобразование Фурье требует арифметических операций.

Задача Прима – Краскала – Кэли. Дано n городов. Нужно соединить все города телефонным кабелем так, чтобы общая длина кабеля была минимальной. Эта задача решается с помощью жадного алгоритма сложности .].

Нахождение эйлерова цикла в графе с m рёбрами. Алгоритм нахождения эйлерова цикла имеет сложность порядка 0(m), см., например.