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 forciclo se ejecuta n veces. Supongo que simplemente no entiendo cómo llegamos a esa conclusión en base a la ifdeclaració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 (porquejse garantiza que es divisible pori).ifdeclaraciones de @ChristophBauer no se ignoran en absoluto . Estaifdeclaración significa que la complejidad es O (n ^ 4) en lugar de O (n ^ 5), porque hace que el ciclo más interno solo ejecuteitiempos en lugar dei*itiempos para cada iteración del segundo ciclo.k < n^2parte, 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 == 0se 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
ifcondición solo es cierta 1 / i de las veces:De hecho,
jsubej < i * i, no solo subej < i. Pero la condiciónj % i == 0es verdadera si y solo sijes un múltiplo dei.Los múltiplos de
identro de la gama soni,2*i,3*i, ...,(i-1) * i. Hayi - 1de estos, por lo que se alcanza el ciclo C ai - 1pesar de losi * i - 1tiempos de iteración del ciclo B.fuente
ifcondició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".niteraciones.n*niteraciones. Imagina el caso cuandoi=n, entoncesj=n*n.niteraciones porque se ejecuta soloiveces, dondeiestá limitadonen 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)dondefestá 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 = 362es 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
ify módulo sin cambiar la complejidadAquí está el método original:
Si usted está confundido por el
ify módulo, sólo puede refactorizar a la basura, conjsaltar directamente deia2*ia3*i...:Para que sea aún más fácil calcular la complejidad, puede introducir una
j2variable 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.printlnpara verificar que eli, j, ktriplete 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
nenteros 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 3731aparecen en "Números de Stirling del primer tipo: s (n + 2, n)". , con dos0s agregados al principio. Significa quesumes 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 loopscombinados 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 loopque recorre de 0 ajif y solo ifj % i == 0. Comojva del 1 ali^2,j % i == 0es cierto para lositiempos. Como lai loopiteración terminan, tienes uno extraO(n).Entonces tienes
O(n^3)dei and j loopsy otroO(n)dek looppara un gran total deO(n^4)fuente
j % i == 0solo 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.illega a 5, por lo que losjbucles 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 * ieso, sería un número constante y no estaría vinculadon, por lo que seríaO(1). Si estás pensando enj < i * ivs.j <= i * i, eso no importará mucho, ya que habrányn-1operaciones, pero en las Grandes-OH notación, ambos mediosO(n)