int sum = 0;
for(int i = 1; i < n; i++) {
for(int j = 1; j < i * i; j++) {
if(j % i == 0) {
for(int k = 0; k < j; k++) {
sum++;
}
}
}
}
Eu não entendo como quando j = i, 2i, 3i ... o último for
loop é executado n vezes. Acho que simplesmente não entendo como chegamos a essa conclusão com base na if
declaração.
Edit: Eu sei como calcular a complexidade de todos os loops, exceto por que o último loop é executado i vezes com base no operador mod ... Eu simplesmente não vejo como é eu. Basicamente, por que não posso subir até i * i em vez de eu?
for (j = i; j < i *i; j += i)
então você não precisa do teste de módulo (porquej
é garantido que é divisível pori
).if
declarações @ChristophBauer não são absolutamente ignoradas. Essaif
declaração significa que a complexidade é O (n ^ 4) em vez de O (n ^ 5), porque faz com que o loop mais interno execute apenas osi
tempos em vez de osi*i
tempos para cada iteração do segundo loop.k < n^2
Então é O (n ^ 5), mas o conhecimento (ao entender oif
) sugere O (n ^ 4).Respostas:
Vamos rotular os loops A, B e C:
j % i == 0
é avaliado, o que leva tempo O (1).Multiplicando tudo isso juntos, obtemos O ( n × i 2 × (1 + i )) = O ( n × i 3 ). Como i é, em média, O ( n ), este é O ( n 4 ).
A parte complicada disso é dizer que a
if
condição é verdadeira apenas 1 / i do tempo:De fato,
j
sobej < i * i
, não apenasj < i
. Mas a condiçãoj % i == 0
é verdadeira se e somente sej
for um múltiplo dei
.Os múltiplos de
i
dentro do intervalo sãoi
,2*i
,3*i
, ...,(i-1) * i
. Existemi - 1
esses, portanto, o loop C é atingidoi - 1
vezes apesar dosi * i - 1
tempos de iteração do loop B.fonte
if
condição leva tempo O (1) em todas as iterações do loop B. Ela é dominada pelo loop C aqui, mas eu contei isso acima, então está apenas "mostrando meu trabalho".n
iterações.n*n
iterações. Imagine o caso quandoi=n
, entãoj=n*n
.n
iterações porque é executado apenas algumasi
vezes, ondei
é limitadon
no pior dos casos.Assim, a complexidade do código é O (n × n × n × n).
Espero que isso ajude você a entender.
fonte
Todas as outras respostas estão corretas, só quero alterar o seguinte. Eu queria ver se a redução de execuções do k-loop interno era suficiente para reduzir a complexidade real abaixo.
O(n⁴).
Então, escrevi o seguinte:Depois de executar isso, torna-se óbvio que a complexidade é de fato
n⁴
. As últimas linhas de saída são assim:O que isso mostra é que a diferença relativa real entre o valor real
n⁴
e a complexidade desse segmento de código é um fator assintótico em relação a um valor próximo0.124...
(na verdade, 0,125). Embora não nos dê o valor exato, podemos deduzir o seguinte:A complexidade do tempo é
n⁴/8 ~ f(n)
ondef
está sua função / método.~
definição do limite dos dois lados do operando é igual. Ou:(Escolhi 363 como limite superior excluído, porque
n = 362
é o último valor para o qual obtemos um resultado razoável. Depois disso, excedemos o espaço longo e o valor relativo se torna negativo.)O usuário kaya3 descobriu o seguinte:
fonte
n⁴/8 + o(n⁴)
, mas é possível dar uma expressão mais rígidan⁴/8 + O(n³)
com O grande de qualquer maneira.Retirar
if
e module sem alterar a complexidadeAqui está o método original:
Se você está confuso com o
if
módulo e, basta refatorá-lo,j
saltando diretamente dei
para2*i
para3*i
...:Para facilitar ainda mais o cálculo da complexidade, é possível introduzir uma
j2
variável intermediária , para que cada variável de loop seja incrementada em 1 a cada iteração:Você pode usar a depuração ou a velha escola
System.out.println
para verificar se oi, j, k
trigêmeo é sempre o mesmo em cada método.Expressão de formulário fechado
Conforme mencionado por outros, você pode usar o fato de que a soma dos primeiros
n
números inteiros é igual an * (n+1) / 2
(consulte números triangulares ). Se você usar essa simplificação para cada loop, obterá:Obviamente, não é a mesma complexidade que o código original, mas retorna os mesmos valores.
Se você pesquisar os primeiros termos no Google, poderá notar que eles
0 0 0 2 11 35 85 175 322 546 870 1320 1925 2717 3731
aparecem em "Números Stirling do primeiro tipo: s (n + 2, n)". , com dois0
s adicionados no início. Isso significa quesum
é o número de Stirling do primeiro tipos(n, n-2)
.fonte
Vamos dar uma olhada nos dois primeiros loops.
O primeiro é simples, está repetindo de 1 a n. O segundo é mais interessante. Vai de 1 a i ao quadrado. Vamos ver alguns exemplos:
No total, o
i and j loops
combinado tem1^2 + 2^2 + 3^2
.Existe uma fórmula para a soma dos primeiros n quadrados
n * (n+1) * (2n + 1) / 6
, que é aproximadamenteO(n^3)
.Você tem um último
k loop
que executa um loop de 0 aj
se e somente sej % i == 0
. Desdej
vai de 1 ai^2
,j % i == 0
é verdade pori
vezes. Como ai loop
iteração terminan
, você tem um extraO(n)
.Então você tem
O(n^3)
a partiri and j loops
e outraO(n)
a partirk loop
de um total deO(n^4)
fonte
j % i == 0
somente quando j for 5, 10, 15, 20 e 25. 5 vezes, como o valor de i. Se você escrever os números de 1 a 25 em 5 x 5 quadrados, apenas a quinta coluna conterá os números divisíveis por 5. Isso funciona para qualquer número de i. Desenhe um quadrado de n por n usando os números 1 a n ^ 2. A enésima coluna conterá os números divisíveis por n. Você tem n linhas, portanto n números de 1 a n ^ 2 são divisíveis por n.i
atinge 5; portanto, osj
loops de 1 a 25, você não pode escolher um número arbitrário. Se o seu 2º loop for para um número fixo, por exemplo, 24, em vez dei * i
, esse seria um número constante e não estaria vinculadon
, como seriaO(1)
. Se você está pensando emj < i * i
vs.j <= i * i
, isso não importará muito, pois haverá operaçõesn
en-1
, mas na notação Big-oh, os dois meiosO(n)