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++;
}
}
}
}
Nie rozumiem, jak kiedy j = i, 2i, 3i ... ostatnia for
pętla działa n razy. Chyba po prostu nie rozumiem, jak doszliśmy do tego wniosku na podstawie tego if
oświadczenia.
Edycja: Wiem, jak obliczyć złożoność dla wszystkich pętli, z wyjątkiem tego, dlaczego ostatnia pętla wykonuje czasy i w oparciu o operator mod ... Po prostu nie rozumiem, jak to jest. Zasadniczo, dlaczego j% nie mogę przejść do i * i zamiast ja?
for (j = i; j < i *i; j += i)
wtedy nie potrzebujesz testu modułu (ponieważj
gwarantuje się podzielność przezi
).if
wypowiedzi są nie całkowicie ignorowane. Taif
instrukcja oznacza, że złożoność wynosi O (n ^ 4) zamiast O (n ^ 5), ponieważ powoduje, że wewnętrzna pętla wykonuje tylkoi
czasy zamiasti*i
czasów dla każdej iteracji drugiej pętli.k < n^2
część, więc jest to O (n ^ 5), ale wiedza (przez zrozumienieif
) sugeruje O (n ^ 4).Odpowiedzi:
Oznaczmy pętle A, B i C:
j % i == 0
jest obliczany, co zajmuje czas O (1).Mnożąc to wszystko razem, otrzymujemy O ( n × i 2 × (1 + i )) = O ( n × i 3 ). Ponieważ i wynosi średnio O ( n ), jest to O ( n 4 ).
Problem polega na tym, że
if
warunek jest spełniony tylko 1 / i czasu:W rzeczywistości,
j
nie idź doj < i * i
, nie tylko doj < i
. Ale warunekj % i == 0
jest spełniony tylko wtedy, gdyj
jest wielokrotnościąi
.Wielokrotności
i
w przedziale sąi
,2*i
,3*i
, ...,(i-1) * i
. Sąi - 1
takie, więci - 1
czasy pętli C są osiągane pomimoi * i - 1
czasów iteracji w pętli B.źródło
if
warunek zajmuje czas O (1) przy każdej iteracji pętli B. Jest tutaj zdominowany przez pętlę C, ale policzyłem ją powyżej, więc po prostu „pokazuje moją pracę”.n
iteracje.n*n
iteracje. Wyobraź sobie wtedyi=n
, kiedyj=n*n
.n
iteracje, ponieważ jest wykonywana tylkoi
razy, gdziei
jest ograniczonan
w najgorszym przypadku.Zatem złożoność kodu wynosi O (n × n × n × n).
Mam nadzieję, że to pomoże ci zrozumieć.
źródło
Wszystkie pozostałe odpowiedzi są poprawne, chcę tylko zmienić następujące. Chciałem zobaczyć, czy ograniczenie wykonania wewnętrznej pętli k było wystarczające, aby zmniejszyć rzeczywistą złożoność poniżej.
O(n⁴).
Napisałem więc:Po wykonaniu tego staje się oczywiste, że złożoność jest w rzeczywistości
n⁴
. Ostatnie wiersze danych wyjściowych wyglądają tak:To pokazuje, że faktyczna względna różnica między faktyczną
n⁴
a złożonością tego segmentu kodu jest czynnikiem asymptotycznym względem wartości wokół0.124...
(faktycznie 0,125). Chociaż nie daje nam to dokładnej wartości, możemy wywnioskować, co następuje:Złożoność czasu jest
n⁴/8 ~ f(n)
tam, gdzief
jest twoja funkcja / metoda.~
granica dwóch stron operandu jest równa. Lub:(Wybrałem 363 jako wykluczoną górną granicę, ponieważ
n = 362
jest to ostatnia wartość, dla której otrzymujemy rozsądny wynik. Następnie przekraczamy długą przestrzeń, a wartość względna staje się ujemna.)Użytkownik kaya3 zorientował się, co następuje:
źródło
n⁴/8 + o(n⁴)
, ale możliwe jest nadanie bardziej surowego wyrażenia zan⁴/8 + O(n³)
pomocą dużej litery „ O”.Usuń
if
i modulo bez zmiany złożonościOto oryginalna metoda:
Jeśli jesteś zdezorientowany przez
if
i modulo, możesz po prostu je refaktoryzować,j
skacząc bezpośrednio zi
do2*i
na3*i
...:Aby jeszcze łatwiej obliczyć złożoność, możesz wprowadzić
j2
zmienną pośrednią , aby każda zmienna pętli była zwiększana o 1 przy każdej iteracji:Możesz użyć debugowania lub starej szkoły
System.out.println
, aby sprawdzić, czyi, j, k
tryplet jest zawsze taki sam w każdej metodzie.Forma zamknięta
Jak wspomnieli inni, możesz użyć faktu, że suma pierwszych
n
liczb całkowitych jest równan * (n+1) / 2
(patrz liczby trójkątne ). Jeśli skorzystasz z tego uproszczenia dla każdej pętli, otrzymasz:Oczywiście nie ma takiej samej złożoności jak oryginalny kod, ale zwraca te same wartości.
Jeśli google pierwsze warunki, można zauważyć, że
0 0 0 2 11 35 85 175 322 546 870 1320 1925 2717 3731
pojawiają się w „liczb Stirlinga pierwszego rodzaju: s (n + 2, n).” , z0
dodanymi dwoma s na początku. Oznacza to, żesum
jest to liczba Stirlinga pierwszego rodzajus(n, n-2)
.źródło
Rzućmy okiem na pierwsze dwie pętle.
Pierwszy jest prosty, zapętla się od 1 do n. Drugi jest bardziej interesujący. Zaczyna się od 1 do kwadratu. Zobaczmy kilka przykładów:
W sumie
i and j loops
połączone mają1^2 + 2^2 + 3^2
.Istnieje wzór na sumę pierwszych n kwadratów
n * (n+1) * (2n + 1) / 6
, która jest z grubszaO(n^3)
.Masz ostatnią,
k loop
która zapętla się od 0 doj
wtedy i tylko wtedyj % i == 0
. Ponieważj
trwa od 1 doi^2
,j % i == 0
jest prawdziwe dlai
czasów. Ponieważi loop
iteracja się kończyn
, masz jeden dodatkowyO(n)
.Więc masz
O(n^3)
zi and j loops
i innyO(n)
zk loop
w sumieO(n^4)
źródło
j % i == 0
tylko, gdy j wynosi 5, 10, 15, 20 i 25. 5 razy, podobnie jak wartość i. Jeśli zapisałbyś liczby od 1 do 25 w kwadracie 5 x 5, tylko piąta kolumna zawierałaby liczby podzielne przez 5. Działa to dla dowolnej liczby i. Narysuj kwadrat n przez n, używając liczb od 1 do n ^ 2. N-ta kolumna będzie zawierać liczby podzielne przez n. Masz n wierszy, więc n liczb od 1 do n ^ 2 dzieli się przez n.i
trafi 5, więc wj
pętlach od 1 do 25 nie można wybrać dowolnej liczby. Jeśli twoja druga pętla przejdzie do stałej liczby, np. 24, zamiasti * i
, byłaby to stała liczba i nie byłaby przywiązanan
, więc tak by byłoO(1)
. Jeśli myślisz oj < i * i
kontraj <= i * i
, nie będzie to miało większego znaczenia, jak będzien
in-1
operacje, ale w notacji Big-oh oba oznaczająO(n)