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++;
}
}
}
}
No entiendo cómo cuando j = i, 2i, 3i ... el último for
ciclo se ejecuta n veces. Supongo que simplemente no entiendo cómo llegamos a esa conclusión en base a la if
declaración.
Editar: Sé cómo calcular la complejidad de todos los bucles, excepto por qué el último bucle se ejecuta i veces en función del operador mod ... Simplemente no veo cómo es i. Básicamente, ¿por qué no puedo j% i subir a i * i en lugar de i?
for (j = i; j < i *i; j += i)
entonces no necesita la prueba de módulo (porquej
se garantiza que es divisible pori
).if
declaraciones de @ChristophBauer no se ignoran en absoluto . Estaif
declaración significa que la complejidad es O (n ^ 4) en lugar de O (n ^ 5), porque hace que el ciclo más interno solo ejecutei
tiempos en lugar dei*i
tiempos para cada iteración del segundo ciclo.k < n^2
parte, por lo que es O (n ^ 5) pero el conocimiento (al comprender elif
) sugiere O (n ^ 4).Respuestas:
Etiquetemos los bucles A, B y C:
j % i == 0
se evalúa, lo que lleva O (1) tiempo.Multiplicando todo esto juntos, obtenemos O ( n × i 2 × (1 + i )) = O ( n × i 3 ). Como i es en promedio O ( n ), este es O ( n 4 ).
La parte difícil de esto es decir que la
if
condición solo es cierta 1 / i de las veces:De hecho,
j
subej < i * i
, no solo subej < i
. Pero la condiciónj % i == 0
es verdadera si y solo sij
es un múltiplo dei
.Los múltiplos de
i
dentro de la gama soni
,2*i
,3*i
, ...,(i-1) * i
. Hayi - 1
de estos, por lo que se alcanza el ciclo C ai - 1
pesar de losi * i - 1
tiempos de iteración del ciclo B.fuente
if
condición toma O (1) tiempo en cada iteración del bucle B. Está dominada por el bucle C aquí, pero lo conté arriba, así que solo está "mostrando mi trabajo".n
iteraciones.n*n
iteraciones. Imagina el caso cuandoi=n
, entoncesj=n*n
.n
iteraciones porque se ejecuta soloi
veces, dondei
está limitadon
en el peor de los casos.Por lo tanto, la complejidad del código es O (n × n × n × n).
Espero que esto te ayude a entender.
fuente
Todas las otras respuestas son correctas, solo quiero enmendar lo siguiente. Quería ver si la reducción de las ejecuciones del bucle k interno era suficiente para reducir la complejidad real a continuación.
O(n⁴).
Entonces escribí lo siguiente:Después de ejecutar esto, se hace evidente que la complejidad es de hecho
n⁴
. Las últimas líneas de salida se ven así:Lo que esto muestra es que la diferencia relativa real entre
n⁴
la complejidad real y este segmento de código es un factor asintótico hacia un valor cercano0.124...
(en realidad 0,125). Si bien no nos da el valor exacto, podemos deducir lo siguiente:La complejidad del tiempo es
n⁴/8 ~ f(n)
dondef
está su función / método.~
el límite definido de los dos lados del operando es igual. O:(Elegí 363 como límite superior excluido, porque
n = 362
es el último valor para el que obtenemos un resultado razonable. Después de eso, superamos el espacio largo y el valor relativo se vuelve negativo).El usuario kaya3 descubrió lo siguiente:
fuente
n⁴/8 + o(n⁴)
, pero den⁴/8 + O(n³)
todos modos es posible dar una expresión más estricta con O grande.Eliminar
if
y módulo sin cambiar la complejidadAquí está el método original:
Si usted está confundido por el
if
y módulo, sólo puede refactorizar a la basura, conj
saltar directamente dei
a2*i
a3*i
...:Para que sea aún más fácil calcular la complejidad, puede introducir una
j2
variable intermedia , de modo que cada variable de bucle se incremente en 1 en cada iteración:Puede usar la depuración o la vieja escuela
System.out.println
para verificar que eli, j, k
triplete sea siempre el mismo en cada método.Expresión de forma cerrada
Como lo mencionaron otros, puede usar el hecho de que la suma de los primeros
n
enteros es igual an * (n+1) / 2
(ver números triangulares ). Si usa esta simplificación para cada ciclo, obtiene:Obviamente, no es la misma complejidad que el código original, pero devuelve los mismos valores.
Si googleas los primeros términos, puedes notar que
0 0 0 2 11 35 85 175 322 546 870 1320 1925 2717 3731
aparecen en "Números de Stirling del primer tipo: s (n + 2, n)". , con dos0
s agregados al principio. Significa quesum
es el número de Stirling del primer tipos(n, n-2)
.fuente
Echemos un vistazo a los dos primeros bucles.
El primero es simple, está pasando de 1 a n. El segundo es más interesante. Va de 1 a 1 al cuadrado. Veamos algunos ejemplos:
En total, los
i and j loops
combinados tienen1^2 + 2^2 + 3^2
.Hay una fórmula para la suma de los primeros n cuadrados
n * (n+1) * (2n + 1) / 6
, que es aproximadamenteO(n^3)
.Tienes un último
k loop
que recorre de 0 aj
if y solo ifj % i == 0
. Comoj
va del 1 ali^2
,j % i == 0
es cierto para losi
tiempos. Como lai loop
iteración terminan
, tienes uno extraO(n)
.Entonces tienes
O(n^3)
dei and j loops
y otroO(n)
dek loop
para un gran total deO(n^4)
fuente
j % i == 0
solo cuando j es 5, 10, 15, 20 y 25. 5 veces, como el valor de i. Si escribe los números del 1 al 25 en un cuadrado de 5 x 5, solo la quinta columna contendrá los números divisibles por 5. Esto funciona para cualquier número de i. Dibuja un cuadrado de n por n usando los números 1 a n ^ 2. La enésima columna contendrá los números divisibles por n. Tienes n filas, entonces n números de 1 a n ^ 2 divisibles por n.i
llega a 5, por lo que losj
bucles del 1 al 25 no pueden elegir un número arbitrario. Si su segundo ciclo iría a un número fijo, por ejemplo, 24, en lugar dei * i
eso, sería un número constante y no estaría vinculadon
, por lo que seríaO(1)
. Si estás pensando enj < i * i
vs.j <= i * i
, eso no importará mucho, ya que habrán
yn-1
operaciones, pero en las Grandes-OH notación, ambos mediosO(n)