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

un

gość
1 / ?
powrót do lekcji

Czytanie kodu pod kątem tempa wzrostu

Przegląd kodu dla poprawności kontra Przegląd kodu dla złożoności

Przegląd kodu dla poprawności wyłapuje błędy logiczne: złe warunki, błędy off-by-one, brakujące sprawdzenia null. Świadomy złożoności przegląd kodu wyłapuje inną klasę defektów: kod, który działa poprawnie przy N = 100, ale rośnie katastrofalnie przy N = 100 000.

Wzorzec MOAD-0001: list seen O(N²) vs set seen O(N) — jednowierszowa naprawa


Ta lekcja łączy się z głębszym dochodzeniem w kursie unhamming: The Missing Chapter: Algorithmic Complexity obejmuje Big O w kontekście defektów produkcyjnych, & MOAD-0001: The Sedimentary Defect mapuje wzorzec w 60+ ekosystemach oprogramowania.


Dwie heurystyki przeglądu


Zagnieżdżone pętle mnożą złożoność. Dwie zagnieżdżone pętle nad N elementami tworzą O(N²). Trzy tworzą O(N³). Podczas przeglądu: najpierw policz głębokość zagnieżdżenia pętli.


Sekwencyjny kontener wewnątrz gorącej pętli. Każde sprawdzenie .contains(), .includes(), .find(), lub in list wewnątrz pętli kosztuje O(N) na sprawdzenie. W N iteracjach: O(N²) w sumie. Ten wzorzec pojawia się w kodzie produkcyjnym w dziesiątkach ekosystemów — kompilator Haskell GHC, Python pip, Apache Maven, Rust Cargo, TypeScript, Godot. Ten sam błąd, różne bazy kodów.

Przegląd: find_duplicates

Przejrzyj następującą funkcję Pythona pod kątem złożoności:


def find_duplicates(items):
    seen = []
    duplicates = []
    for item in items:
        if item in seen:
            duplicates.append(item)
        else:
            seen.append(item)
    return duplicates
Wykonaj świadomy złożoności przegląd kodu: (a) Jaka jest czasowa złożoność tej funkcji? (b) Dlaczego? Zidentyfikuj dokładną linię odpowiedzialną. (c) Przepisz ją na O(N).

MOAD-0001: Defekt osadowy

Ten sam defekt, sześćdziesiąt ekosystemów

Wzorzec if x not in list: list.append(x) wewnątrz pętli pojawia się — pojawił się — w kodzie produkcyjnym w dziesiątkach ekosystemów oprogramowania:


- GHC (kompilator Haskell): rozwiązywacz zależności, O(N²) przy N = 50 000 pakietów, budowanie 17 minut

- Python pip: rozwiązywanie konfliktów zależności

- Apache Maven: deduplikacja ścieżki klas

- Rust Cargo: ujednolicenie funkcji

- Kompilator TypeScript: rozwiązywanie modułów

- Godot / silnik gier Redot: przechodzenie grafu węzłów


Żaden z tych zespołów nie popełnił błędu. Pisali poprawny kod przy małych N. N rosnęło. Kod się skrzepł. Wzorzec ma nazwę: MOAD-0001, Defekt osadowy. Osad: poprawny, nieszkodliwy w cienkich warstwach. Z czasem warstwy się gromadzą i twardnieją.


Naprawa

W każdym przypadku: zamień sekwencyjny kontener na kontener mieszający. Jeden wiersz. Zerowa zmiana zachowania przy poprawnych wejściach. Przyspieszenie 100–1000× przy rzeczywistym N.


Naprawa działa, ponieważ dwie operacje muszą pozostać szybkie:

1. Sprawdzenie przynależności: czy ten element był widzany wcześniej?

2. Posortowane wyjście: zwróć elementy w kolejności, w jakiej się pojawiły (czasem wymagane, czasem nie).


Zbiór obsługuje (1) w O(1). Jeśli (2) również ma znaczenie, przechowuj oddzielną listę na wyjście posortowane i zbiór na sprawdzenie przynależności. Dwie struktury danych, każda wykonująca jedno zadanie.

Odpowiadanie recenzentowi

Żądanie ściągnięcia naprawia MOAD-0001 w funkcji przechodzenia grafu projektu. Recenzent komentuje:


> "Zbiory nie zachowują kolejności wstawienia — to zmienia zachowanie."

Jak się odpowiadasz? Wyjaśnij, kiedy obawy recenzenta mają zastosowanie, a kiedy nie. Zaproponuj rozwiązanie, które spełnia obie obawy, gdy się one konfliktują.

Wzorzec analizy rozmowy kwalifikacyjnej

Oczekiwany format

Analiza złożoności algorytmicznej pojawia się na rozmowach kwalifikacyjnych w inżynierii oprogramowania. Silna odpowiedź następuje czteroczęściowy wzorzec:


1. Określ bieżącą złożoność — O(?) na czas, O(?) na miejsce.

2. Wyjaśnij dlaczego — zidentyfikuj konkretną konstrukcję odpowiadającą (zagnieżdżona pętla, skan liniowy w pętli, gałęziowanie rekurencyjne).

3. Zaproponuj optymalizację — nazwij zmianę i strukturę danych lub algorytm, który wprowadza.

4. Określ nową złożoność — po naprawie, jaka jest złożoność czasu i miejsca?


Przykład:

Kod: [funkcja, która sprawdza przynależność na liście wewnątrz pętli]
Bieżąca: O(N²) — `item in seen_list` to O(N) na sprawdzenie × N iteracji
Optymalizacja: zamień seen_list na seen_set (zbiór mieszający)
Po: O(N) — sprawdzenie przynależności do zbioru to O(1)

Ta umiejętność przenosi się poza rozmowy: świadomy złożoności przegląd kodu, projektowanie architektury, debugowanie wydajności, audyty bezpieczeństwa. Każdy system, który przetwarza dane wejściowe o zmiennym rozmiarze, na tym korzysta.


Ta lekcja łączy się dalej z kursem unhamming, który stosuje ten dokładny wzorzec do dochodzenia defektów produkcyjnych w 60+ projektach open-source.

Rozmowa kwalifikacyjna: analiza has_common_element

Zastosuj czteroczęściowy format rozmowy do tej funkcji:


def has_common_element(list_a, list_b):
    for item in list_a:
        for other in list_b:
            if item == other:
                return True
    return False
Określ bieżącą złożoność, wyjaśnij dlaczego, zaproponuj optymalizację, określ nową złożoność.