English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

nu

gość
1 / ?
powrót do lekcji

Tempa wzrostu, nie absolutne koszty

Big O: Pomiar tempa wzrostu kosztów

Notacja Big O mierzy tempo wzrostu kosztu algorytmu — nie koszt absolutny.

Pytanie, na które odpowiada Big O: gdy rozmiar danych wejściowych N się podwoi, czy praca się podwoi? Czworokrotnie? Zostanie taka sama?

Litera 'O' oznacza 'rząd wielkości'. Wyrażenie w nawiasach opisuje kształt krzywej wzrostu.

Tablica Big O: operacje przy N=10, 100 i 1 000 dla O(1), O(N) i O(N²)

Kluczowe klasy złożoności:

- O(1) — stała: koszt nie rośnie. Przykład: wyszukanie wartości po indeksie w tablicy. Niezależnie od tego, czy tablica ma 10 czy 10 milionów elementów, jedno wyszukanie to jedno wyszukanie.

- O(N) — liniowa: koszt rośnie z danymi wejściowymi. Przykład: przeczytanie każdego elementu na liście raz. Podwój listę, podwój odczyty.

- O(N²) — kwadratowa: koszt rośnie jako kwadrat danych wejściowych. Przykład: porównanie każdego elementu z każdym innym elementem. Podwój N, czworokrotnie wzrośnie praca.

Tabela porównania wzrostu:

NO(1)O(N)O(N²)
10110100
100110010 000
1 00011 0001 000 000

Przy N=10 różnica wygląda na małą. Przy N=1 000 przepaść staje się ogromna.

Porównaj O(N) do O(N²)

Użyj tabeli porównania wzrostu powyżej.

Przy N=1 000: O(N) wymaga 1 000 operacji. O(N²) wymaga 1 000 000 operacji.

Przy N=1 000, ile razy więcej operacji wymaga O(N²) w porównaniu do O(N)? Pokaż swoją pracę.

Jak struktura kodu ujawnia złożoność

Jak zidentyfikować Big O z kodu

Big O chowa się w kształcie twojego kodu. Cztery wzorce obejmują większość przypadków:

Jedna pętla przez N elementów: O(N)

# O(N): jeden przebieg przez listę
for item in list_of_n_items:
    process(item)

Zagnieżdżone pętle, każda przez N elementów: O(N²)

# O(N²): każdy element sparowany z każdym innym elementem
for item_a in list_of_n_items:
    for item_b in list_of_n_items:
        compare(item_a, item_b)

Wyszukiwanie w stałym czasie: O(1)

Dostęp do indeksu tablicy, wyszukiwanie w tabeli mieszającej — jeden krok niezależnie od rozmiaru.

Dziel i podbijaj (zmniejszanie problemu o połowę w każdym kroku): O(log N)

Wyszukiwanie binarne: każde sprawdzenie zmniejsza o połowę pozostałe elementy.

---

Ukryte O(N²): lista wewnątrz pętli

# Wygląda to jak O(N), ale to faktycznie O(N²)
visited = []
for item in list_of_n_items:
    if item not in visited:   # skanuje wszystkie elementy visited: O(N)
        visited.append(item)  # cała pętla: O(N²)

Linia if item not in visited skanuje każdy element visited w każdej iteracji. Skanowanie listy kosztuje O(N). Pętla, która działa N razy, każda wykonując O(N) pracy: O(N) × O(N) = O(N²).

Ten wzorzec pojawia się w 1 000+ projektów open-source. Naprawa zajmuje jeden znak: zamień visited = [] na visited = set(). Sprawdzenie przynależności do zestawu kosztuje O(1), nie O(N). Jedna zmiana. Takie same wyniki. 1 000× szybciej przy N=1 000.

Klasyfikuj i napraw ten kod

Przeczytaj ten kod:

result = []
for name in student_names:
    if name not in result:
        result.append(name)
Jaka jest złożoność Big O tego kodu? Wyjaśnij, dlaczego linia `if name not in result` czyni go drogim. Następnie przepisz kod, aby uczynić go O(N).

Teoria spotyka praktykę

Big O w prawdziwym świecie

Big O nie jest tylko teorią. Rozdziela programy, które kończą się w sekundach, od programów, które zajmują 17 minut — na dokładnie tym samym zadaniu.

Rzeczywisty przykład: detekcja cyklu zależności w systemie budowania.

Gdy instalujesz oprogramowanie, komputer rozwiązuje graf zależności: pakiet A potrzebuje B, B potrzebuje C i tak dalej. System budowania sprawdza ten graf pod kątem cykli.

Algorytm detekcji cykli O(N²) działa dobrze przy N=100 pakietach. Przy N=50 000 pakietów (rzeczywisty projekt): zajmuje 17 minut. Naprawa O(N): 10 sekund.

Ten dokładnie defekt istnieje w GHC (kompilator Haskella), pip (menedżer pakietów Pythona), Maven (system budowania Javy), Cargo (menedżer pakietów Rusta) i 1 000+ innych projektach.

Nie dlatego, że ich autorzy byli niedbali. visited = [] zostało napisane, gdy N było małe. Kod zamarł. N wzrosło. Nikt tego nie zauważył, aż budowanie zajęło pół godziny.

Big O to słownictwo, które pozwala ci nazwać to, co się stało — i to naprawić.

---

Chcesz się zagłębić bardziej? Nasz kurs unhamming zawiera pełny rozdział o Big O, MOAD-0001 (rzeczywisty defekt O(N²) znaleziony w 1 000+ projektach open-source) i dlaczego nazwanie wzorca pozwala ci go znaleźć wszędzie. Patrz The Missing Chapter: Algorithmic Complexity.

Przewidź czasy budowania

System budowania używa detekcji cykli O(N²).

Zmierzone czasy budowania:

- N=100 pakietów: 0,01 sekund

- N=1 000 pakietów: 1 sekunda

Dla O(N²): czas skaluje się jako (N_new / N_old)².

Dla O(N): czas skaluje się jako (N_new / N_old).

Używając tych wzorów i zmierzonych danych: (A) przy N=10 000, ile czasu zajmuje wersja O(N²)? (B) po naprawie O(N), używając N=1 000 jako linii bazowej, ile czasu zajmuje wersja O(N) przy N=10 000? Pokaż swoją pracę dla obu.