Определение сложности рекурсивных функций (нотация Big O)

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

int recursiveFun1(int n)
{
    if (n <= 0)
        return 1;
    else
        return 1 + recursiveFun1(n-1);
}

int recursiveFun2(int n)
{
    if (n <= 0)
        return 1;
    else
        return 1 + recursiveFun2(n-5);
}

int recursiveFun3(int n)
{
    if (n <= 0)
        return 1;
    else
        return 1 + recursiveFun3(n/5);
}

void recursiveFun4(int n, int m, int o)
{
    if (n <= 0)
    {
        printf("%d, %dn",m, o);
    }
    else
    {
        recursiveFun4(n-1, m+1, o);
        recursiveFun4(n-1, m, o+1);
    }
}

int recursiveFun5(int n)
{
    for (i = 0; i < n; i += 2) {
        // do something
    }

    if (n <= 0)
        return 1;
    else
        return 1 + recursiveFun5(n-5);
}

3 ответов


сложность времени в большой o-нотации для каждой функции находится в числовом порядке:

  1. первая функция вызывается рекурсивно n раз до достижения базового случая, поэтому ее O(n), часто называют линейный.
  2. вторая функция называется n-5 для каждого времени, поэтому мы вычитаем пять из n перед вызовом функции, но n-5 также O(n). (На самом деле называется Порядком n/5 раз. И, O(n/5) = O (n) ).
  3. эта функция log (n) база 5, за каждый раз делим на 5 перед вызовом функции, поэтому его O(log(n))(база 5), часто называемая логарифмические и чаще всего Big O notation и анализ сложности использует базу 2.
  4. в-четвертых, это O(2^n) или экспоненциальный, так как каждый вызов функции вызывает себя дважды, если он не был рекурсирован n

для случая, когда n <= 0, T(n) = O(1). Поэтому сложность времени будет зависеть от того, когда n >= 0.

рассмотрим случай n >= 0 в части ниже.

1.

T(n) = a + T(n - 1)

где A-некоторая константа.

по индукции:

T(n) = n * a + T(0) = n * a + b = O(n)

где a, b-некоторая константа.

2.

T(n) = a + T(n - 5)

где A-некоторая константа

By индукция:

T(n) = ceil(n / 5) * a + T(k) = ceil(n / 5) * a + b = O(n)

где a, b-некоторая константа и k

3.

T(n) = a + T(n / 5)

где A-некоторая константа

по индукции:

T(n) = a * log5(n) + T(0) = a * log5(n) + b = O(log n)

где a, b-некоторая константа

4.

T(n) = a + 2 * T(n - 1)

где A-некоторая константа

по индукции:

T(n) = a + 2a + 4a + ... + 2^n * a + T(0) * 2 ^ n 
     = a * 2^(n+1) - a + b * 2 ^ n
     = (2 * a + b) * 2 ^ n - a
     = O(2 ^ n)

где a, b-некоторая константа.

5.

T(n) = n / 2 + T(n - 5)

мы можем доказать индукцией, что T(5k) >= T(5k - d) где d = 0, 1, 2, 3, 4

написать n = 5m - b (m, b-целое число; b = 0, 1, 2, 3, 4), тогда m = (n + b) / 5:

T(n) = T(5m - b) <= T(5m)

(на самом деле, чтобы быть более строгим здесь, новую функцию T'(n) следует определить так, что T'(5r - q) = T(5r) здесь q = 0, 1, 2, 3, 4. Мы знаем!--21--> как доказано выше. Когда мы знаем, что T'(n) находится в O(f), что означает, что существует постоянная a, b так что T'(n) <= a * f(n) + b мы можем сделать вывод, что T(n) <= a * f(n) + b и поэтому T(n) находится в O(f). Этот шаг не совсем необходимо, но легче думать, когда не приходится иметь дело с остальным.)

расширения T(5m):

T(5m) = 5m / 2 + T(5m - 5) 
      = (5m / 2 + 5 / 2) * m / 2 + T(0) 
      = O(m ^ 2) = O(n ^ 2)

таким образом, T(n) is O(n ^ 2).


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

Complexity = length of tree from root node to leaf node * number of leaf nodes
  1. первая функция будет иметь длину n и номер конечного узла 1 Так что сложность будет n*1 = n
  2. вторая функция будет иметь длину n/5 и количество листовых узлов снова 1 Так что сложность будет n/5 * 1 = n/5. Он должен быть приближен к n

  3. для третьей функции, так как n делится на 5 при каждом рекурсивном вызове, длина рекурсивного дерева будет log(n)(base 5), а количество листовых узлов опять 1 так что сложность будет log(n)(base 5) * 1 = log(n)(base 5)

  4. для четвертой функции, поскольку каждый узел будет иметь два дочерних узла, количество листовых узлов будет равно (2^n) и длина рекурсивного дерева будет n таким образом, сложность будет (2^n) * n. Но с n ничтожно перед (2^n), его можно игнорировать, а сложность можно только сказать, что (2^n).

  5. для пятой функции, есть два элемента представляя сложности. Сложность, введенная рекурсивной природой функции, и сложность, введенная for петли в каждой функции. При выполнении вышеуказанного вычисления сложность, введенная рекурсивным характером функции, будет ~ n и сложность из-за for loop n. Общая сложность будет n*n.

Примечание: это быстрый и грязный способ расчета сложности(ничего официального!). Хотел бы услышать отзывы об этом. Спасибо.