Perché la complessità computazionale è O(n^4)?
``java int sum = 0; for(int i = 1; i < n; i++) { for(int j = 1; j < i * i; j++) { se(j % i == 0) { for(int k = 0; k < j; k++) { sum++; } } } }
Non capisco come quando j = i, 2i, 3i... l'ultimo ciclo `for` viene eseguito n volte. Credo di non capire come siamo arrivati a questa conclusione basandoci sulla dichiarazione `if`.
Edit: So come calcolare la complessità per tutti i cicli, tranne che per il motivo per cui l'ultimo ciclo esegue i volte in base all'operatore mod... Non vedo proprio come sia i. In pratica, perché j % i non può arrivare a i * i piuttosto che i?
33
5
Etichettiamo i cicli A, B e C:
``java int sum = 0; // ciclo A for(int i = 1; i < n; i++) { // ciclo B for(int j = 1; j < i * i; j++) { if(j % i == 0) { // ciclo C for(int k = 0; k < j; k++) { sum++; } } } }
n
iterazioni.n*n
iterazioni. Immaginate il caso in cuii=n
, quindij=n*n
.n
iterazioni perché viene eseguito soloi
volte, dovei
è limitato an
nel caso peggiore.Quindi, la complessità del codice è O(n×n×n×n).
Spero che questo vi aiuti a capire.
Tutte le altre risposte sono corrette, voglio solo modificare quanto segue. Volevo vedere se la riduzione delle esecuzioni del k-loop interno era sufficiente a ridurre l'effettiva complessità al di sotto della "O" (n⁴).` Così ho scritto quanto segue:
Diamo un'occhiata ai primi due cicli.
Il primo è semplice, va da 1 a n. Il secondo è più interessante. Va da 1 a i al quadrato. Vediamo alcuni esempi:
In totale, i
i e j loop
combinati hanno1^2 + 2^2 + 3^2
.C'è una formula per la somma dei primi n quadrati,
n * (n+1) * (2n + 1) / 6
, che è circaO(n^3)
.Hai un ultimo
k loop
che va da 0 aj
se e solo sej % i == 0
. Poichéj
va da 1 ai^2
,j % i == 0
è vero peri
volte. Poiché ili loop
itera sun
, hai unO(n)
in più.Quindi avete
O(n^3)
dai e j loop
e un altroO(n)
dalk loop
per un totale diO(n^4)
Rimuovere "se" e modulo senza modificare la complessità
Ecco il metodo originale:
Se siete confusi dal "se" e dal modulo, potete semplicemente rifattorizzarli via, con il "j" che salta direttamente da "i" a "2i" a "3i" ... :
Per rendere ancora più facile il calcolo della complessità, è possibile introdurre una variabile intermedia
j2
, in modo che ogni variabile del loop venga incrementata di 1 ad ogni iterazione:È possibile utilizzare il debug o la vecchia scuola
System.out.println
per verificare chei, j, k
tripletta sia sempre la stessa in ogni metodo.Espressione in forma chiusa
Come detto da altri, si può usare il fatto che la somma dei primi
n
interi è uguale an * (n+1) / 2
(vedi numeri triangolari). Se si utilizza questa semplificazione per ogni ciclo, si ottiene :Ovviamente non è non la stessa complessità del codice originale ma restituisce gli stessi valori.
Se cercate su Google i primi termini, potete notare che
0 0 0 0 2 11 35 85 175 322 546 870 1320 1925 2717 3731
appaiono in "numeri di Stirling del primo tipo: s(n+2, n)", con due0
s aggiunti all'inizio. Significa chesum
è il numero di Stirling del primo tipos(n, n-2)
.