ВПЕРЁД

НАЗАД



Сложность программ с циклами

Невложенный цикл for

for i = 0..n-1
    do_something(i)

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

Вложенные циклы for

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

for i = 0..n-1
    for j = 0..n-1
        do_something(i, j)

В представленном случае каждый из циклов содержит итераций, тогда всего итераций с вызовом do_something(i, j) в алгоритме будет , откуда следует .

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

for i = 0..n-1
    for j = 0..n-1
        for k = 0..n-1
            do_something(i, j, k) // O(n^3)


for i = 0..n-1
    for j = 0..n-1
        for k = 0..n-1
            for l = 0..n-1
                do_something(i, j, k, l) // O(n^4)

Сложный случай вложенных циклов

Предположим, что имеется следующая ситуация:

for i = 0..n-1
    for j = 0..i
        do_something(i, j)

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

Грубая оценка временной сложности

Число итераций вложенного цикла for j = 0..i для каждой из итераций внешнего цикла for i = 0..n-1 не превышает . То есть временная сложность работы алгоритма не будет превышать временную сложность ранее рассмотренного алгоритма:

for i = 0..n-1
    for j = 0..n-1
        do_something(i, j)

Из этого следует: .

Точная оценка временной сложности

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

, где - временная сложность -й итерации внешнего цикла.

Элементы отображают число итераций внутреннего цикла и изменяются следующим образом:

, , …,

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

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

Таким образом, для нашего исследуемого алгоритма получаем:

Графический способ получения оценки сложности вычислений

На рисунке-гистограмме показаны элементы упомянутой арифметической прогрессии, которая отражает процесс изменения количества итераций внутреннего цикла программы с ростом порядкового номера итерации внешнего цикла.

for i = 0..n-1
    for j = 0..i
        do_something(i, j)

Необходимо оценить сумму указанных элементов. Сумма элементов - площадь фигуры, образованной синими столбцами (площадь синего треугольника). Очевидно, что площадь синего треугольника - половина площади красного квадрата (в котором расположен треугольник). Площадь красного квадрата равна , тогда площадь синего треугольника равна . Таким образом, получаем:

.

Цикл while, вложенный в цикл for

for i = 0..n-1
    j = 0
    while j*j <= i // граница i будет достигнута при j равном
                   // квадратному корню из i
        do_something(i, j)
        j = j + 1

Упрощаем процесс анализа путём замены условия в цикле while:

for i = 0..n-1
    j = 0
    while j*j <= n // граница n будет достигнута при j равном
                   // квадратному корню из n
        do_something(i, j)
        j = j + 1



ВПЕРЁД

НАЗАД