← Strona główna

15 — Zadania egzaminacyjne — mix

Zestawy zadań z poprzednich sesji. Praktyka pod warunki egzaminu INF.04.

Egzamin i wsparcie

Jak wygląda egzamin INF.04 — część praktyczna?

Egzamin praktyczny INF.04 trwa 150 minut. Zdający otrzymuje zadanie do wykonania na komputerze — zazwyczaj jedno większe lub kilka mniejszych powiązanych.

  • Zadanie zawiera opis problemu i przykładowe dane wejściowe/wyjściowe
  • Piszesz program w wybranym języku — na tej stronie: Python
  • Program musi działać poprawnie dla danych testowych
  • Oceniana jest poprawność działania i częściowo jakość kodu
  • Możesz korzystać z dokumentacji i zasobów na stanowisku
  • Czas to kluczowy czynnik — ćwicz pisanie pod presją czasu
Strategia na egzaminie Przeczytaj zadanie dwa razy. Zacznij od wczytania danych i wypisania ich (sprawdź format). Potem dodawaj logikę krok po kroku. Testuj na przykładach z treści. Zostaw 15 minut na sprawdzenie.
Typowe obszary zadań egzaminacyjnych
# Obszar 1: Przetwarzanie danych z pliku
# - Wczytaj dane z pliku CSV/TXT
# - Oblicz statystyki (min, max, średnia)
# - Posortuj i wypisz wyniki
# - Zapisz przetworzone dane do nowego pliku

# Obszar 2: Algorytmy
# - Implementacja algorytmu sortowania
# - Wyszukiwanie (liniowe lub binarne)
# - NWD, NWW, liczby pierwsze
# - Rekurencja (silnia, Fibonacci)

# Obszar 3: Aplikacja konsolowa
# - Menu z pętlą while
# - Walidacja danych wejściowych
# - Operacje na listach/słownikach
# - Formatowanie wyjścia w tabeli

# Obszar 4: Aplikacja webowa (Flask)
# - Prosta strona z formularzem
# - Wyświetlanie danych z listy/słownika
# - Podstawowy CRUD
# - Szablony Jinja2

Zestaw 1 — Przetwarzanie danych uczniów

Zadanie 1A Wczytaj, oblicz, zapisz średnie

Plik uczniowie.txt zawiera dane uczniów — każda linia to: imię, nazwisko i trzy oceny oddzielone przecinkami. Wczytaj plik, oblicz średnią każdego ucznia. Wypisz na ekran uczniów z wyróżnieniem najlepszego. Zapisz do pliku wyniki.txt posortowaną listę: miejsce, imię i nazwisko, średnia.

Rozwiązanie
def wczytaj_uczniow(nazwa_pliku):
    uczniowie = []
    try:
        with open(nazwa_pliku, "r",
                  encoding="utf-8") as f:
            for linia in f:
                linia = linia.strip()
                if not linia:
                    continue
                czesci   = linia.split(",")
                imie     = czesci[0].strip()
                nazwisko = czesci[1].strip()
                oceny    = [int(x.strip())
                            for x in czesci[2:]]
                srednia  = sum(oceny) / len(oceny)
                uczniowie.append({
                    "imie":     imie,
                    "nazwisko": nazwisko,
                    "oceny":    oceny,
                    "srednia":  round(srednia, 2)
                })
    except FileNotFoundError:
        print(f"Błąd: brak pliku {nazwa_pliku}")
    return uczniowie

def zapisz_wyniki(uczniowie, nazwa_pliku):
    posortowani = sorted(uczniowie,
                         key=lambda u: u["srednia"],
                         reverse=True)
    with open(nazwa_pliku, "w",
              encoding="utf-8") as f:
        f.write(f"{'Msc':<5} {'Imię i nazwisko':<25}"
                f" {'Średnia':>8}\n")
        f.write("-" * 40 + "\n")
        for i, u in enumerate(posortowani, 1):
            pelne = f"{u['imie']} {u['nazwisko']}"
            f.write(f"{i:<5} {pelne:<25}"
                    f" {u['srednia']:>8.2f}\n")
    return posortowani

# Generuj przykładowy plik
with open("uczniowie.txt", "w",
          encoding="utf-8") as f:
    f.write("Kasia, Nowak, 5, 4, 5\n")
    f.write("Marek, Kowal, 3, 4, 3\n")
    f.write("Zofia, Wiśniewska, 5, 5, 4\n")
    f.write("Piotr, Zając, 2, 3, 4\n")

uczniowie    = wczytaj_uczniow("uczniowie.txt")
posortowani  = zapisz_wyniki(uczniowie, "wyniki.txt")

print(f"{'Msc':<5} {'Imię i nazwisko':<25} {'Śr':>6}")
print("-" * 38)
for i, u in enumerate(posortowani, 1):
    pelne  = f"{u['imie']} {u['nazwisko']}"
    marker = " ← NAJLEPSZY" if i == 1 else ""
    print(f"{i:<5} {pelne:<25} "
          f"{u['srednia']:>6.2f}{marker}")
Omówienie — kluczowe elementy
  1. Parsowanie linii CSV ręcznie
    linia.split(",") dzieli po przecinku. Pierwsze dwa pola to imię i nazwisko, reszta to oceny — czesci[2:] pobiera wszystkie od trzeciego elementu. List comprehension z int(x.strip()) konwertuje każdą.
  2. Funkcje dla każdej operacji
    Oddzielamy wczytywanie od zapisywania — każda funkcja robi jedną rzecz. Łatwiej testować i modyfikować. Na egzaminie czytelny kod to dodatkowe punkty.
  3. Obsługa FileNotFoundError
    Zamiast crashować — informujemy użytkownika i zwracamy pustą listę. Program nie przerywa działania przy brakującym pliku.
  4. Marker najlepszego
    if i == 1 po posortowaniu — pierwszy element to najlepszy uczeń. Prosty warunek, duże wrażenie przy wypisywaniu.
Zadanie 1B Analiza statystyczna klasy średnie

Na podstawie tych samych danych wypisz: średnią klasy, medianę średnich, odchylenie standardowe, ile uczniów ma średnią powyżej średniej klasy, jaka to wartość procentowa.

Rozwiązanie
import math

def statystyki_klasy(uczniowie):
    if not uczniowie:
        return
    srednie = [u["srednia"] for u in uczniowie]
    n       = len(srednie)

    # Średnia klasy
    sr_klasy = sum(srednie) / n

    # Mediana
    posort = sorted(srednie)
    if n % 2 == 0:
        mediana = (posort[n//2-1] + posort[n//2]) / 2
    else:
        mediana = posort[n//2]

    # Odchylenie standardowe
    wariancja = sum((x - sr_klasy)**2
                    for x in srednie) / n
    odch_std  = math.sqrt(wariancja)

    # Powyżej średniej
    pow_sr = [u for u in uczniowie
              if u["srednia"] > sr_klasy]

    print("\n=== STATYSTYKI KLASY ===")
    print(f"Liczba uczniów:      {n}")
    print(f"Średnia klasy:       {sr_klasy:.2f}")
    print(f"Mediana:             {mediana:.2f}")
    print(f"Odchylenie std:      {odch_std:.2f}")
    print(f"Powyżej średniej:    "
          f"{len(pow_sr)} "
          f"({len(pow_sr)/n*100:.1f}%)")

statystyki_klasy(uczniowie)
Omówienie — kluczowe elementy
  1. Odchylenie standardowe bez modułu statistics
    Wzór: sqrt(Σ(xi - mean)² / n). Generator expression wewnątrz sum() oblicza każdy składnik. math.sqrt() pierwiastkuje wynik. Nie potrzebujemy importu statistics.
  2. Mediana dla parzystej i nieparzystej liczby
    Dwa przypadki: nieparzysta n — środkowy element. Parzysta n — średnia dwóch środkowych. Sortujemy kopię (sorted()) żeby nie modyfikować oryginału.
  3. Formatowanie procentu
    len(pow_sr)/n*100:.1f — ręczne obliczenie procenta z jednym miejscem po przecinku. Alternatywa: :.1% ale wtedy nie mnożymy przez 100.

Zestaw 2 — Algorytmy i liczby

Zadanie 2A Liczby doskonałe i zaprzyjaźnione średnie

Napisz program który: (a) znajdzie wszystkie liczby doskonałe do 10000, (b) sprawdzi czy dwie wczytane liczby są zaprzyjaźnione (suma dzielników właściwych pierwszej = druga i odwrotnie). Wypisz wyniki w czytelnym formacie.

Rozwiązanie
def suma_dzielnikow(n):
    """Suma dzielników właściwych n (bez n)."""
    if n <= 1:
        return 0
    suma = 1
    i    = 2
    while i * i <= n:
        if n % i == 0:
            suma += i
            if i != n // i:
                suma += n // i
        i += 1
    return suma

def czy_doskonala(n):
    return n > 1 and suma_dzielnikow(n) == n

def czy_zaprzyjaznione(a, b):
    return (a != b and
            suma_dzielnikow(a) == b and
            suma_dzielnikow(b) == a)

# Liczby doskonałe do 10000
print("Liczby doskonałe do 10000:")
for n in range(2, 10001):
    if czy_doskonala(n):
        dziel = [i for i in range(1, n)
                 if n % i == 0]
        print(f"  {n} = {' + '.join(map(str,dziel))}")

# Liczby zaprzyjaźnione
print("\nSprawdzanie zaprzyjaźnienia:")
a = int(input("Pierwsza liczba: "))
b = int(input("Druga liczba: "))

if czy_zaprzyjaznione(a, b):
    print(f"{a} i {b} są zaprzyjaźnione!")
    print(f"  Suma dzielników {a}: "
          f"{suma_dzielnikow(a)}")
    print(f"  Suma dzielników {b}: "
          f"{suma_dzielnikow(b)}")
else:
    print(f"{a} i {b} NIE są zaprzyjaźnione.")
    print(f"  σ({a}) = {suma_dzielnikow(a)}")
    print(f"  σ({b}) = {suma_dzielnikow(b)}")
Omówienie — kluczowe elementy
  1. Suma dzielników do √n
    Iterujemy tylko do √n — jeśli i dzieli n, to n/i też jest dzielnikiem. Dodajemy oba jednocześnie. Warunek i != n//i zapobiega podwójnemu liczeniu dzielnika równego √n.
  2. Funkcje jednolinijkowe przez złożone warunki
    czy_doskonala i czy_zaprzyjaznione to eleganckie jednowierszowe funkcje. Warunek a != b wyklucza przypadek gdy obie liczby są równe.
  3. Wypisanie rozkładu na dzielniki
    List comprehension zbiera wszystkie dzielniki właściwe, join() łączy je znakiem + . Wynik: 6 = 1 + 2 + 3.
Zadanie 2B Spirala liczb — macierz 2D trudne

Wczytaj n. Wypełnij macierz n×n liczbami od 1 do n² spiralnie (od lewego górnego rogu, w prawo, potem w dół, w lewo, w górę...). Wypisz macierz w czytelnym formacie.

Rozwiązanie
def spirala(n):
    # Pusta macierz n×n
    m = [[0] * n for _ in range(n)]

    # Kierunki: prawo, dół, lewo, góra
    dr = [0,  1, 0, -1]
    dc = [1,  0, -1, 0]

    r = c = kierunek = 0

    for liczba in range(1, n*n + 1):
        m[r][c] = liczba

        # Następna pozycja
        nr = r + dr[kierunek]
        nc = c + dc[kierunek]

        # Zmień kierunek jeśli wyjście poza macierz
        # lub komórka już wypełniona
        if (nr < 0 or nr >= n or
            nc < 0 or nc >= n or
            m[nr][nc] != 0):
            kierunek = (kierunek + 1) % 4
            nr = r + dr[kierunek]
            nc = c + dc[kierunek]

        r, c = nr, nc
    return m

n = int(input("Rozmiar spirali n: "))
m = spirala(n)

# Wypisanie
szerokosc = len(str(n * n))
for wiersz in m:
    print(" ".join(f"{x:{szerokosc}}"
                   for x in wiersz))
Omówienie — kluczowe elementy
  1. Wektory kierunków
    Cztery kierunki ruchu zakodowane jako zmiany wiersza (dr) i kolumny (dc). Kierunek 0=prawo, 1=dół, 2=lewo, 3=góra. Zmiana kierunku: (kierunek+1) % 4.
  2. Warunek zmiany kierunku
    Zmieniamy kierunek gdy wychodzimy poza macierz (indeks poza zakresem) LUB gdy komórka jest już wypełniona (≠0). Elegancki warunek obsługuje oba przypadki.
  3. Dynamiczna szerokość kolumn
    szerokosc = len(str(n*n)) — dla n=4 największa liczba to 16 (2 cyfry). Formatowanie f"{x:{szerokosc}}" wyrównuje wszystkie liczby do tej samej szerokości.

Zestaw 3 — Aplikacja konsolowa z menu

Zadanie 3 System zarządzania biblioteką trudne

Napisz program z menu: (1) dodaj książkę (tytuł, autor, rok), (2) wyświetl wszystkie posortowane po autorze, (3) wyszukaj po tytule lub autorze (częściowe dopasowanie), (4) usuń książkę po ID, (5) zapisz do pliku, (6) wczytaj z pliku, (0) wyjście.

Rozwiązanie
import csv, os

biblioteka = []
next_id    = 1

def dodaj(tytul, autor, rok):
    global next_id
    biblioteka.append({
        "id":    next_id,
        "tytul": tytul,
        "autor": autor,
        "rok":   rok
    })
    next_id += 1
    print(f"Dodano: '{tytul}'")

def wyswietl():
    if not biblioteka:
        print("Biblioteka jest pusta.")
        return
    posort = sorted(biblioteka,
                    key=lambda k: k["autor"])
    print(f"\n{'ID':<5} {'Autor':<20} "
          f"{'Tytuł':<25} {'Rok':>5}")
    print("-" * 57)
    for k in posort:
        print(f"{k['id']:<5} {k['autor']:<20} "
              f"{k['tytul']:<25} {k['rok']:>5}")

def szukaj(fraza):
    fraza  = fraza.lower()
    wyniki = [k for k in biblioteka
              if fraza in k["tytul"].lower()
              or fraza in k["autor"].lower()]
    if wyniki:
        print(f"Znaleziono {len(wyniki)} wyników:")
        for k in wyniki:
            print(f"  [{k['id']}] {k['autor']} "
                  f"— {k['tytul']} ({k['rok']})")
    else:
        print("Brak wyników.")

def usun(book_id):
    global biblioteka
    przed = len(biblioteka)
    biblioteka = [k for k in biblioteka
                  if k["id"] != book_id]
    if len(biblioteka) < przed:
        print(f"Usunięto książkę ID={book_id}")
    else:
        print(f"Brak książki o ID={book_id}")

def zapisz(plik="biblioteka.csv"):
    with open(plik, "w", newline="",
              encoding="utf-8") as f:
        writer = csv.DictWriter(
            f, fieldnames=["id","tytul",
                           "autor","rok"])
        writer.writeheader()
        writer.writerows(biblioteka)
    print(f"Zapisano {len(biblioteka)} książek.")

def wczytaj(plik="biblioteka.csv"):
    global biblioteka, next_id
    if not os.path.exists(plik):
        print("Brak pliku."); return
    with open(plik, "r", newline="",
              encoding="utf-8") as f:
        reader = csv.DictReader(f)
        biblioteka = [{
            "id":    int(r["id"]),
            "tytul": r["tytul"],
            "autor": r["autor"],
            "rok":   int(r["rok"])
        } for r in reader]
    if biblioteka:
        next_id = max(k["id"]
                      for k in biblioteka) + 1
    print(f"Wczytano {len(biblioteka)} książek.")

# Menu główne
while True:
    print("\n=== BIBLIOTEKA ===")
    print("1. Dodaj książkę")
    print("2. Wyświetl wszystkie")
    print("3. Wyszukaj")
    print("4. Usuń książkę")
    print("5. Zapisz do pliku")
    print("6. Wczytaj z pliku")
    print("0. Wyjście")
    wybor = input("Wybór: ").strip()

    if wybor == "1":
        t = input("Tytuł: ").strip()
        a = input("Autor: ").strip()
        r = int(input("Rok:   "))
        dodaj(t, a, r)
    elif wybor == "2":
        wyswietl()
    elif wybor == "3":
        szukaj(input("Szukaj: "))
    elif wybor == "4":
        usun(int(input("ID do usunięcia: ")))
    elif wybor == "5":
        zapisz()
    elif wybor == "6":
        wczytaj()
    elif wybor == "0":
        print("Do widzenia!"); break
    else:
        print("Nieznana opcja.")
Omówienie — kluczowe elementy
  1. Każda operacja to osobna funkcja
    Menu wywołuje funkcje — nie zawiera logiki. Funkcje są niezależne i testowalne osobno. To wzorzec który bardzo dobrze sprawdza się na egzaminie.
  2. Wyszukiwanie częściowe
    fraza in k["tytul"].lower() — operator in sprawdza czy fraza jest podciągiem. .lower() zapewnia wyszukiwanie bez rozróżniania wielkości liter.
  3. Odtworzenie next_id po wczytaniu
    next_id = max(k["id"] for k in biblioteka) + 1 — nowe ID musi być większe od wszystkich istniejących. Bez tego po wczytaniu z pliku ID mogłyby się powtarzać.
  4. csv.DictWriter i DictReader
    Zapis i odczyt przez słowniki — czytelniejsze niż indeksowanie po pozycji. fieldnames definiuje kolejność kolumn w pliku CSV.

Zadania do samodzielnego rozwiązania — pełne zestawy egzaminacyjne

Każde zadanie poniżej to pełnoprawne zadanie egzaminacyjne. Rozwiązuj je w warunkach zbliżonych do egzaminu — ustaw timer na 30–45 minut i nie zaglądaj do notatek.

1★★☆

Sklep — zestawienie sprzedaży

Plik CSV: produkt, kategoria, cena, ilość. Wczytaj, oblicz przychód dla każdego produktu i każdej kategorii. Wypisz top 3 produktów i najlepszą kategorię. Zapisz raport do TXT.

Wskazówka: csv.DictReader, słownik kategorii, sorted z key, format tabeli
2★★☆

Kalkulator ONP

Zaimplementuj kalkulator działający w Odwrotnej Notacji Polskiej (np. "3 4 + 2 *" = 14). Używaj stosu (lista). Obsłuż błędy: dzielenie przez 0, za mało operandów.

Wskazówka: split(), pętla, cyfry na stos, operator — pop() dwa razy i push wynik
3★★☆

Harmonogram zajęć

Przechowuj plan lekcji jako słownik {dzień: [lista przedmiotów]}. Zaimplementuj: dodaj/usuń przedmiot, wypisz plan na dany dzień, znajdź w którym dniu jest dany przedmiot.

Wskazówka: słownik zagnieżdżony, walidacja dnia tygodnia, menu w pętli
4★★★

Szyfrowanie XOR

Zaimplementuj szyfrowanie XOR: każdy znak tekstu XORuj z odpowiadającym znakiem klucza (klucz powtarzaj). Zaszyfruj i odszyfruj tekst. Wypisz hex każdego bajtu.

Wskazówka: ord(), ^ operator XOR, chr(), klucz: klucz[i % len(klucz)]
5★★★

Generator sudoku — walidator

Wczytaj planszę sudoku 9×9 (0 = puste pole). Sprawdź poprawność: czy każdy wiersz, kolumna i kwadrat 3×3 zawiera cyfry 1–9 bez powtórzeń (ignoruj zera). Wypisz które reguły są naruszone.

Wskazówka: set do sprawdzenia unikalności, trzy osobne funkcje: wiersze, kolumny, kwadraty
6★★★

Aplikacja Flask — ankieta

Aplikacja webowa: strona z ankietą (3–5 pytań wielokrotnego wyboru), zbieranie odpowiedzi, wyświetlanie statystyk (ile % wybrało każdą opcję) na osobnej stronie wyników.

Wskazówka: słownik liczników odpowiedzi, request.form, szablony Jinja2 z pętlami, procenty