Kurs podstawy programowania UMCS, zdjęcie laptopa.

Podstawy programowania – Laboratorium 5

Powtórzenie: od specyfikacji do obrony rozwiązania

To laboratorium nie dokłada dużej porcji nowej składni. Łączy dane, obliczenia, decyzje i pętle oraz ćwiczy najważniejszą umiejętność przed kolokwium: samodzielne kontrolowanie poprawności programu.

Powtórzenie nie polega na ponownym czytaniu składni

Na tym etapie powinieneś umieć połączyć poznane elementy. Trudność zadania zwykle nie wynika już z pojedynczego operatora, lecz z wyboru właściwego stanu, rozbicia specyfikacji na przypadki i utrzymania poprawnego znaczenia zmiennych w pętli.

  • tworzyć testy przed napisaniem programu,
  • odróżniać błąd składni, typu, modelu i algorytmu,
  • naprawiać program na podstawie objawów i śladu wykonania,
  • łączyć warunki z iteracją bez gubienia przypadków brzegowych,
  • wyjaśniać rozwiązanie oraz modyfikować je bez generowania całości od nowa,
  • rozpoznawać oczywiście zbędną pracę programu.

Zasada powtórzenia: mniej czasu poświęcaj na przepisywanie gotowych rozwiązań, a więcej na przewidywanie, śledzenie, testowanie, poprawianie i uzasadnianie.

Stała procedura rozwiązywania problemu

  1. Zapisz wejście, wynik i ograniczenia.
  2. Policz ręcznie mały przykład.
  3. Nazwij stan i przypadki.
  4. Napisz kod, testy i obroń decyzje.

Jeżeli korzystasz z AI, pokaż narzędziu własną specyfikację i testy, a nie samo krótkie polecenie „napisz program”. Po otrzymaniu kodu sprawdź każdą zmienną, warunek i granicę pętli. Na kolokwium, czy podczas obrony rozwiązania na innych labolatoriach musisz umieć samodzielnie wyjaśnić wynik i wykonać małą zmianę.

Na co zwracać uwagę przy powtórzeniu?

ObszarPytanie kontrolne
specyfikacjaCo dokładnie jest wejściem, wynikiem i co oznacza każdy przypadek?
typy i jednostkiCzy zakres, znak, dzielenie i format odpowiadają danym?
decyzjeCzy gałęzie obejmują granice i wyjątki oraz czy mają dobrą kolejność?
pętlaCo jest prawdziwe po każdej iteracji i dlaczego pętla się zakończy?
testyCzy sprawdziłem zero, jeden element, wartości ujemne i granice przedziałów?
narzędziaCzy kompiluję z ostrzeżeniami i umiem użyć debuggera do obserwacji stanu?

Naprawianie programu jest pełnoprawnym zadaniem programistycznym

Poniższy program miał wczytać dodatnie n liczb całkowitych, a następnie wypisać maksimum i średnią. Kompiluje się, lecz zawiera kilka niezależnych błędów. Najpierw przygotuj wejście, na którym każdy z nich będzie widoczny.

1. Diagnoza statystyk

Kolokwium-styledebugowanieintegracja tematów
#include <stdio.h>

int main(void) {
    int n;
    scanf("%d", &n);

    int sum = 0;
    int maximum = 0;

    for (int i = 0; i <= n; i = i + 1) {
        int value;
        scanf("%d", &value);
        sum = sum + value;

        if (value > maximum) {
            maximum = value;
        }
    }

    double average = sum / n;
    printf("max=%d, srednia=%.2f\n", maximum, average);
    return 0;
}

Znajdź błąd granicy pętli, inicjalizacji maksimum i dzielenia. Zastanów się również, co program powinien zrobić dla n <= 0 oraz czy suma wielu wartości zmieści się w typie int.

Poprawiona wersja
#include <stdio.h>

int main(void) {
    int n;
    scanf("%d", &n);

    if (n <= 0) {
        printf("Brak danych.\n");
        return 0;
    }

    int value;
    scanf("%d", &value);

    long long sum = value;
    int maximum = value;

    for (int i = 1; i < n; i = i + 1) {
        scanf("%d", &value);
        sum = sum + value;

        if (value > maximum) {
            maximum = value;
        }
    }

    double average = (double)sum / n;
    printf("max=%d, srednia=%.2f\n", maximum, average);
    return 0;
}

Naprawy nie są przypadkowymi zmianami znaków. Każda wynika z własności danych: dokładnie n odczytów, maksimum należące do wejścia i średnia obliczona jako dzielenie zmiennoprzecinkowe.


Nowy algorytm zbudowany ze znanych elementów

Największy wspólny dzielnik można obliczyć bez sprawdzania wszystkich kandydatów. Algorytm Euklidesa wielokrotnie zastępuje parę (a, b) parą (b, a % b). Gdy druga wartość staje się zerem, pierwsza jest wynikiem.

2. Największy wspólny dzielnik

Kolokwium-stylewhile%ślad algorytmu

Wczytaj dwie nieujemne liczby całkowite, nie obie równe zero, i oblicz ich NWD. Przed uruchomieniem wykonaj ręczny ślad dla 48 i 18: (48,18) → (18,12) → (12,6) → (6,0).

Rozwiązanie
#include <stdio.h>

int main(void) {
    unsigned int a, b;
    scanf("%u %u", &a, &b);

    while (b != 0) {
        unsigned int remainder = a % b;
        a = b;
        b = remainder;
    }

    printf("NWD: %u\n", a);
    return 0;
}

W każdej iteracji NWD pary się nie zmienia, ale druga liczba maleje i pozostaje nieujemna, więc algorytm dochodzi do końca. To krótkie uzasadnienie mówi więcej o rozwiązaniu niż samo zapamiętanie czterech instrukcji.

Audyt rozwiązania AI: wynik i koszt

Kod może być częściowo poprawny: działać dla typowych danych, ale zawierać pominięty przypadek albo wykonywać niepotrzebnie wiele operacji. Przeanalizuj poniższą próbę testu pierwszości.

Czy „działa dla 7” wystarcza?

AI-auditprzypadek brzegowykoszt
#include <stdio.h>

int main(void) {
    int number;
    scanf("%d", &number);

    int is_prime = 1;
    for (int divisor = 2; divisor < number; divisor = divisor + 1) {
        if (number % divisor == 0) {
            is_prime = 0;
        }
    }

    if (is_prime) {
        printf("pierwsza\n");
    } else {
        printf("niepierwsza\n");
    }

    return 0;
}
  1. Jaki wynik program poda dla 1, 0 i liczby ujemnej?
  2. Ile potencjalnych dzielników sprawdzi dla dużej liczby pierwszej n?
  3. Jak ograniczyć zakres dzielników, wykorzystując pary czynników?
  4. Czy po znalezieniu dzielnika dalsze sprawdzanie może zmienić odpowiedź?

Wniosek: inicjalizacja is_prime = 1 błędnie klasyfikuje wszystkie liczby mniejsze od 2. Ponadto sprawdzanie aż do number - 1 wykonuje znacznie więcej pracy niż rozwiązanie z laboratorium 4. Audyt powinien obejmować zarówno poprawność, jak i oczywisty koszt.

Przed Kolokwium I

Nie ucz się programów na pamięć. Powinieneś umieć zbudować prosty program od specyfikacji, prześledzić cudzy kod i wskazać przypadek obalający błędne rozwiązanie. Szczególną uwagę poświęć granicom pętli, inicjalizacji stanu, dzieleniu całkowitemu i pełności warunków.

Krótka próba samodzielności

  • potrafię przewidzieć wynik krótkiego programu bez uruchamiania,
  • potrafię podać znaczenie każdej zmiennej w pętli,
  • potrafię wskazać, dlaczego pętla się zakończy,
  • potrafię przygotować test zwykły, graniczny i rozróżniający,
  • potrafię poprawić jeden fragment programu bez generowania go ponownie,
  • potrafię obronić typy, warunki i granice użyte w rozwiązaniu.

Większość pracy na tym laboratorium odbywa się na Platformie do ćwiczeń. Wpis jest listą kontrolną i punktem odniesienia; właściwe utrwalenie powstaje przez samodzielne rozwiązywanie, testowanie i omawianie różnych problemów.


Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *