Eu tenho um curso de Ciência da Computação amanhã e preciso de ajuda para determinar a complexidade dessas funções recursivas. Sei como resolver casos simples, mas ainda estou tentando aprender como resolver esses casos mais difíceis. Esses foram apenas alguns dos problemas de exemplo que não consegui descobrir. Qualquer ajuda seria muito apreciada e ajudaria muito em meus estudos, obrigado!
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, %d\n",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);
}
recursion
big-o
complexity-theory
Michael_19
fonte
fonte
Respostas:
A complexidade do tempo, na notação Big O, para cada função, está em ordem numérica:
O(n)
, o que é frequentemente chamado de linear .O(n)
. (Realmente chamado de ordem de n / 5 vezes. E, O (n / 5) = O (n)).O(log(n))
(base 5), geralmente chamada logarítmica e, na maioria das vezes, a análise de notação e complexidade Big O, usa a base 2.O(2^n)
, ou exponencial , uma vez que cada chamada de função chama-se duas vezes menos que tenha sido recursed n vezes.Quanto à última função, o loop for leva n / 2, pois estamos aumentando em 2, e a recursão leva n-5, e, como o loop for é chamado recursivamente, a complexidade do tempo é (n-5) * (n / 2) = (2n-10) * n = 2n ^ 2-10n, devido ao comportamento assintótico e às considerações de pior cenário, ou ao limite superior pelo qual o grande O está se esforçando, estamos interessados apenas no maior termo
O(n^2)
.Boa sorte em seus períodos intermediários;)
fonte
n/2
iterações dofor
loop, por que diminuir em 5 não resultaria emn/5
chamadas recursivas? Isso ainda resultaria em,O(n^2)
mas parece uma explicação mais intuitiva. Por que misturar subtração e divisão quando são essenciais para fazer a mesma coisa?n/5
nãon-5
. E, finalmente, todo se resume aO(N^2)
.Para o caso em que
n <= 0
,T(n) = O(1)
. Portanto, a complexidade do tempo dependerá de quandon >= 0
.Vamos considerar o caso
n >= 0
na parte abaixo.1
onde a é alguma constante.
Por indução:
onde a, b são constantes.
2)
onde a é constante
Por indução:
onde a, b são constantes e k <= 0
3)
onde a é constante
Por indução:
onde a, b são constantes
4)
onde a é constante
Por indução:
onde a, b são constantes.
5)
onde n é alguma constante
Reescreva
n = 5q + r
onde q e r são inteiros er = 0, 1, 2, 3, 4Temos
q = (n - r) / 5
, e como r <5, podemos considerá-lo uma constante, entãoq = O(n)
Por indução:
Como r <4, podemos encontrar b constante para que
b >= T(r)
fonte
Uma das melhores maneiras que encontro para aproximar a complexidade do algoritmo recursivo é desenhar a árvore da recursão. Depois de ter a árvore recursiva:
n
e número de nós de folha,1
portanto a complexidade serán*1 = n
A segunda função terá o comprimento
n/5
e o número de nós de folhas novamente,1
assim será a complexidaden/5 * 1 = n/5
. Deve ser aproximado paran
Para a terceira função, como
n
está sendo dividido por 5 em cada chamada recursiva, o comprimento da árvore recursiva serálog(n)(base 5)
e o número de nós das folhas novamente 1, para que a complexidade seja reduzida.log(n)(base 5) * 1 = log(n)(base 5)
Para a quarta função, uma vez que cada nó terá dois nós filhos, o número de nós folha será igual
(2^n)
e o comprimento da árvore recursiva serán
a complexidade(2^n) * n
. Mas, comon
é insignificante na frente(2^n)
, pode ser ignorado e só se pode dizer que é complexidade(2^n)
.Para a quinta função, existem dois elementos que introduzem a complexidade. Complexidade introduzida pela natureza recursiva da função e complexidade introduzida pelo
for
loop em cada função. Fazendo o cálculo acima, a complexidade introduzida pela natureza recursiva da função será~ n
e a complexidade será devido ao loop forn
. Total complexidade serán*n
.Nota: Essa é uma maneira rápida e suja de calcular a complexidade (nada oficial!). Gostaria de ouvir comentários sobre isso. Obrigado.
fonte
2^n
, a altura da árvore deve sern
, nãolog n
. A altura seria apenaslog n
sen
representasse o número total de nós na árvore. Mas isso não acontece.Podemos provar matematicamente o que estava faltando nas respostas acima.
Isso pode ajudar muito a entender como calcular qualquer método. Eu recomendo lê-lo de cima para baixo para entender completamente como fazê-lo:
T(n) = T(n-1) + 1
Isso significa que o tempo que leva para o método terminar é igual ao mesmo método, mas com n-1 que éT(n-1)
e agora adicionamos+ 1
porque é o tempo que leva para que as operações gerais sejam concluídas (excetoT(n-1)
). Agora, vamos encontrarT(n-1)
as seguintes:T(n-1) = T(n-1-1) + 1
. Parece que agora podemos formar uma função que pode nos dar algum tipo de repetição para que possamos entender completamente. Vamos colocar o lado direitoT(n-1) = ...
em vez deT(n-1)
dentro do métodoT(n) = ...
que nos dará:T(n) = T(n-1-1) + 1 + 1
que éT(n) = T(n-2) + 2
ou em outras palavras, precisamos encontrar o nosso faltandok
:T(n) = T(n-k) + k
. O próximo passo é tomarn-k
e afirmar que,n-k = 1
porque no final da recursão, será necessário exatamente O (1) quandon<=0
. A partir desta equação simples, sabemos agora issok = n - 1
. Vamos colocark
no nosso método final: oT(n) = T(n-k) + k
que nos dará: oT(n) = 1 + n - 1
que é exatamenten
ouO(n)
.O(n)
.T(n) = T(n/5) + 1
como antes, o tempo para o término desse método é igual ao tempo do mesmo método, mas com on/5
qual é limitadoT(n/5)
. Vamos encontrarT(n/5)
como em 1:T(n/5) = T(n/5/5) + 1
qual éT(n/5) = T(n/5^2) + 1
. Vamos lugarT(n/5)
dentroT(n)
para o cálculo final:T(n) = T(n/5^k) + k
. Novamente, como antes,n/5^k = 1
que én = 5^k
exatamente o mesmo que perguntar qual é o poder de 5, nos dará n, a resposta élog5n = k
(logaritmo da base 5). Vamos colocar nossas descobertas emT(n) = T(n/5^k) + k
como segue:T(n) = 1 + logn
o que éO(logn)
T(n) = 2T(n-1) + 1
o que temos aqui é basicamente o mesmo de antes, mas desta vez estamos invocando o método 2 vezes recursivamente, multiplicando-o por 2. Vamos descobrirT(n-1) = 2T(n-1-1) + 1
qual éT(n-1) = 2T(n-2) + 1
. Nosso próximo lugar como antes, vamos colocar nossa descoberta: oT(n) = 2(2T(n-2)) + 1 + 1
que éT(n) = 2^2T(n-2) + 2
isso que nos dáT(n) = 2^kT(n-k) + k
. Vamos descobrirk
reivindicando on-k = 1
que ék = n - 1
. Vamos colocar dak
seguinte forma:T(n) = 2^(n-1) + n - 1
que é aproximadamenteO(2^n)
T(n) = T(n-5) + n + 1
É quase o mesmo que 4, mas agora adicionamosn
porque temos umfor
loop. Vamos descobrirT(n-5) = T(n-5-5) + n + 1
qual éT(n-5) = T(n - 2*5) + n + 1
. Vamos colocá-lo:T(n) = T(n-2*5) + n + n + 1 + 1)
qual éT(n) = T(n-2*5) + 2n + 2)
e para o k:T(n) = T(n-k*5) + kn + k)
novamente: on-5k = 1
que én = 5k + 1
isso aproximadamenten = k
. Isso nos dará: oT(n) = T(0) + n^2 + n
que é mais ou menosO(n^2)
.Agora, recomendo a leitura do restante das respostas, que agora oferecem uma perspectiva melhor. Boa sorte em ganhar os grandes O's :)
fonte
A chave aqui é visualizar a árvore de chamadas. Feito isso, a complexidade é:
o último termo pode ser calculado da mesma maneira que fazemos para uma função iterativa normal.
Em vez disso, o total de nós de uma árvore completa é calculado como
Onde C é o número de filhos de cada nó e L é o número de níveis da árvore (raiz incluída).
É fácil visualizar a árvore. Comece na primeira chamada (nó raiz) e desenhe um número de filhos igual ao número de chamadas recursivas na função. Também é útil escrever o parâmetro passado para a sub-chamada como "valor do nó".
Então, nos exemplos acima:
fonte