29  Řešení cvičení

Tato příloha obsahuje řešení vybraných cvičení z jednotlivých kapitol.

29.1 Část 0: Začínáme s Pythonem

29.1.1 Kapitola 0.1: Instalace a první kroky

Cvičení 1: Převod teplot

def celsius_to_fahrenheit(c):
    return c * 9/5 + 32

# Test
print(celsius_to_fahrenheit(0))    # 32
print(celsius_to_fahrenheit(100))  # 212
print(celsius_to_fahrenheit(37))   # 98.6

Cvičení 2: BMI kalkulačka

def bmi(vaha_kg, vyska_m):
    return vaha_kg / (vyska_m ** 2)

# Test
print(f"BMI: {bmi(70, 1.75):.1f}")  # BMI: 22.9

29.1.2 Kapitola 0.2: Základy programování

Cvičení 1: Součet sudých čísel

def suma_sudych(n):
    return sum(x for x in range(1, n+1) if x % 2 == 0)

# Alternativně
def suma_sudych_v2(n):
    return sum(range(2, n+1, 2))

print(suma_sudych(10))  # 30 (2+4+6+8+10)

Cvičení 2: Nalezení maxima

def najdi_max(seznam):
    if not seznam:
        return None
    maximum = seznam[0]
    for x in seznam:
        if x > maximum:
            maximum = x
    return maximum

print(najdi_max([3, 1, 4, 1, 5, 9, 2, 6]))  # 9

29.2 Část I: Funkce a grafy

29.2.1 Kapitola 1: Souřadnicový systém

Cvičení 1: Vzdálenost bodů

import numpy as np

def vzdalenost(A, B):
    """Euklidovská vzdálenost mezi body A a B."""
    return np.sqrt((B[0] - A[0])**2 + (B[1] - A[1])**2)

A = (1, 2)
B = (4, 6)
print(f"Vzdálenost: {vzdalenost(A, B)}")  # 5.0

Cvičení 2: Střed úsečky

def stred(A, B):
    """Střed úsečky AB."""
    return ((A[0] + B[0]) / 2, (A[1] + B[1]) / 2)

print(stred((0, 0), (4, 6)))  # (2.0, 3.0)

29.2.2 Kapitola 2: Co je funkce

Cvičení 1: Tabulka hodnot funkce

def tabulka(f, a, b, n=10):
    """Vytvoří tabulku hodnot funkce f na intervalu [a, b]."""
    x = np.linspace(a, b, n)
    for xi in x:
        print(f"f({xi:.2f}) = {f(xi):.4f}")

# Příklad použití
tabulka(lambda x: x**2, 0, 5)

Cvičení 2: Inverzní funkce

# f(x) = 2x + 3
# y = 2x + 3
# x = (y - 3) / 2
# f^(-1)(y) = (y - 3) / 2

def f(x):
    return 2*x + 3

def f_inv(y):
    return (y - 3) / 2

# Ověření: f(f_inv(y)) = y
y = 10
print(f"f(f_inv({y})) = {f(f_inv(y))}")  # 10.0

29.2.3 Kapitola 3: Lineární funkce

Cvičení 1: Rovnice přímky ze dvou bodů

def primka_z_bodu(A, B):
    """Vrátí koeficienty a, b přímky y = ax + b procházející body A, B."""
    x1, y1 = A
    x2, y2 = B
    a = (y2 - y1) / (x2 - x1)
    b = y1 - a * x1
    return a, b

A = (1, 2)
B = (3, 6)
a, b = primka_z_bodu(A, B)
print(f"y = {a}x + {b}")  # y = 2.0x + 0.0

Cvičení 2: Průsečík přímek

def prusecik(a1, b1, a2, b2):
    """Průsečík přímek y = a1*x + b1 a y = a2*x + b2."""
    if a1 == a2:
        return None  # Rovnoběžné
    x = (b2 - b1) / (a1 - a2)
    y = a1 * x + b1
    return (x, y)

# y = 2x + 1 a y = -x + 4
print(prusecik(2, 1, -1, 4))  # (1.0, 3.0)

29.2.4 Kapitola 4: Nelineární funkce

Cvičení 1: Vrchol paraboly

def vrchol_paraboly(a, b, c):
    """Vrchol paraboly y = ax^2 + bx + c."""
    x_v = -b / (2*a)
    y_v = a * x_v**2 + b * x_v + c
    return (x_v, y_v)

# y = x^2 - 4x + 3
print(vrchol_paraboly(1, -4, 3))  # (2.0, -1.0)

Cvičení 2: Zdvojovací čas

# Exponenciální růst: y = y0 * 2^(t/T)
# kde T je zdvojovací čas

# Pro y = y0 * e^(kt), zdvojovací čas je T = ln(2) / k

import numpy as np

def zdvojovaci_cas(k):
    """Zdvojovací čas pro exponenciální růst e^(kt)."""
    return np.log(2) / k

# Příklad: růst 5% ročně = e^(0.05*t)
print(f"Zdvojovací čas: {zdvojovaci_cas(0.05):.2f} let")  # ~13.86 let

29.3 Část II: Vektory a matice

29.3.1 Kapitola 5: Vektory

Cvičení 1: Délka vektoru

import numpy as np

def delka(v):
    """Euklidovská délka (L2 norma) vektoru."""
    return np.sqrt(np.sum(v**2))

v = np.array([3, 4])
print(f"Délka: {delka(v)}")  # 5.0

Cvičení 2: Jednotkový vektor

def jednotkovy(v):
    """Normalizace vektoru na délku 1."""
    return v / np.linalg.norm(v)

v = np.array([3, 4])
u = jednotkovy(v)
print(f"Jednotkový: {u}")           # [0.6, 0.8]
print(f"Délka: {np.linalg.norm(u)}")  # 1.0

29.3.2 Kapitola 6: Operace s vektory

Cvičení 1: Úhel mezi vektory

def uhel(u, v):
    """Úhel mezi vektory ve stupních."""
    cos_theta = np.dot(u, v) / (np.linalg.norm(u) * np.linalg.norm(v))
    return np.degrees(np.arccos(np.clip(cos_theta, -1, 1)))

u = np.array([1, 0])
v = np.array([1, 1])
print(f"Úhel: {uhel(u, v):.1f}°")  # 45.0°

Cvičení 2: Kosínová podobnost

def kosinova_podobnost(u, v):
    """Kosínová podobnost mezi vektory."""
    return np.dot(u, v) / (np.linalg.norm(u) * np.linalg.norm(v))

# Podobné vektory
a = np.array([1, 2, 3])
b = np.array([2, 4, 6])
print(f"Podobnost: {kosinova_podobnost(a, b):.4f}")  # 1.0

# Kolmé vektory
c = np.array([1, 0])
d = np.array([0, 1])
print(f"Podobnost: {kosinova_podobnost(c, d):.4f}")  # 0.0

29.3.3 Kapitola 7: Matice

Cvičení 1: Součet řádků a sloupců

A = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])

print("Součet řádků:", np.sum(A, axis=1))     # [6, 15, 24]
print("Součet sloupců:", np.sum(A, axis=0))  # [12, 15, 18]

Cvičení 2: Diagonální prvky

A = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])

print("Hlavní diagonála:", np.diag(A))          # [1, 5, 9]
print("Vedlejší diagonála:", np.diag(np.fliplr(A)))  # [3, 5, 7]
print("Stopa matice:", np.trace(A))             # 15

29.3.4 Kapitola 8: Násobení matic

Cvičení 1: Ruční násobení

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# Ruční výpočet
C = np.zeros((2, 2))
for i in range(2):
    for j in range(2):
        for k in range(2):
            C[i, j] += A[i, k] * B[k, j]

print("Ruční výpočet:")
print(C)

# Ověření
print("NumPy:")
print(A @ B)

Cvičení 2: Asociativita

A = np.random.randn(3, 4)
B = np.random.randn(4, 2)
C = np.random.randn(2, 5)

# (AB)C = A(BC)
vysledek1 = (A @ B) @ C
vysledek2 = A @ (B @ C)

print("Rozdíl:", np.max(np.abs(vysledek1 - vysledek2)))  # ~0 (numerická chyba)

29.3.5 Kapitola 9: Lineární transformace

Cvičení 1: Rotační matice

def rotace_matice(uhel_stupne):
    """Matice rotace o daný úhel."""
    theta = np.radians(uhel_stupne)
    return np.array([
        [np.cos(theta), -np.sin(theta)],
        [np.sin(theta), np.cos(theta)]
    ])

# Rotace o 90°
R = rotace_matice(90)
v = np.array([1, 0])
print(f"Rotovaný vektor: {R @ v}")  # [0, 1]

Cvičení 2: Škálování

def skalovani_matice(sx, sy):
    """Matice škálování."""
    return np.array([
        [sx, 0],
        [0, sy]
    ])

S = skalovani_matice(2, 3)
v = np.array([1, 1])
print(f"Škálovaný vektor: {S @ v}")  # [2, 3]

29.4 Část III: Derivace a gradient

29.4.1 Kapitola 10: Změna a rychlost změny

Cvičení 1: Numerická derivace

def numericka_derivace(f, x, h=1e-5):
    """Centrální diference."""
    return (f(x + h) - f(x - h)) / (2 * h)

# f(x) = x^2, f'(x) = 2x
f = lambda x: x**2
x = 3
print(f"Numerická derivace: {numericka_derivace(f, x):.6f}")  # ~6.0
print(f"Analytická derivace: {2*x}")  # 6

Cvičení 2: Průměrná rychlost

def prumerna_rychlost(f, a, b):
    """Průměrná rychlost změny funkce f na intervalu [a, b]."""
    return (f(b) - f(a)) / (b - a)

# Příklad: dráha s(t) = t^2, průměrná rychlost na [0, 2]
s = lambda t: t**2
print(f"Průměrná rychlost: {prumerna_rychlost(s, 0, 2)} m/s")  # 2.0

29.4.2 Kapitola 11: Pravidla derivování

Cvičení 1: Derivace polynomu

def derivace_polynomu(koeficienty):
    """Derivace polynomu zadaného koeficienty [a_0, a_1, ..., a_n]."""
    n = len(koeficienty)
    return [i * koeficienty[i] for i in range(1, n)]

# f(x) = 3 + 2x + x^2 = [3, 2, 1]
# f'(x) = 2 + 2x = [2, 2]
print(derivace_polynomu([3, 2, 1]))  # [2, 2]

Cvičení 2: Řetízkové pravidlo

# f(x) = sin(x^2)
# f'(x) = cos(x^2) * 2x

import numpy as np

def f(x):
    return np.sin(x**2)

def f_derivace(x):
    return np.cos(x**2) * 2*x

x = 1
print(f"Analytická: {f_derivace(x):.6f}")
print(f"Numerická: {numericka_derivace(f, x):.6f}")

29.4.3 Kapitola 12: Funkce více proměnných

Cvičení 1: Parciální derivace

# f(x, y) = x^2 + xy + y^2
# ∂f/∂x = 2x + y
# ∂f/∂y = x + 2y

def f(x, y):
    return x**2 + x*y + y**2

def grad_f(x, y):
    return np.array([2*x + y, x + 2*y])

x, y = 1, 2
print(f"Gradient: {grad_f(x, y)}")  # [4, 5]

Cvičení 2: Numerický gradient

def numerick_gradient(f, point, h=1e-5):
    """Numerický gradient pomocí centrálních diferencí."""
    grad = np.zeros_like(point)
    for i in range(len(point)):
        point_plus = point.copy()
        point_minus = point.copy()
        point_plus[i] += h
        point_minus[i] -= h
        grad[i] = (f(point_plus) - f(point_minus)) / (2 * h)
    return grad

f = lambda p: p[0]**2 + p[1]**2
point = np.array([3.0, 4.0])
print(f"Gradient: {numerick_gradient(f, point)}")  # [6, 8]

29.4.4 Kapitola 13: Automatická derivace

Cvičení 1: PyTorch autograd

import torch

x = torch.tensor([2.0], requires_grad=True)
y = torch.tensor([3.0], requires_grad=True)

# f(x, y) = x^2 * y + y^3
z = x**2 * y + y**3

z.backward()

print(f"∂z/∂x = {x.grad.item()}")  # 2*x*y = 12
print(f"∂z/∂y = {y.grad.item()}")  # x^2 + 3*y^2 = 4 + 27 = 31

29.5 Část IV: Pravděpodobnost a statistika

29.5.1 Kapitola 14: Náhoda a pravděpodobnost

Cvičení 1: Bayesova věta

# P(nemoc) = 0.01
# P(pozitivní|nemoc) = 0.99
# P(pozitivní|zdravý) = 0.05

# P(nemoc|pozitivní) = ?

P_nemoc = 0.01
P_zdravy = 1 - P_nemoc
P_poz_nemoc = 0.99
P_poz_zdravy = 0.05

P_poz = P_poz_nemoc * P_nemoc + P_poz_zdravy * P_zdravy
P_nemoc_poz = (P_poz_nemoc * P_nemoc) / P_poz

print(f"P(nemoc|pozitivní) = {P_nemoc_poz:.4f}")  # ~0.167

Cvičení 2: Monte Carlo simulace

import numpy as np

def odhadni_pi(n_vzorku):
    """Odhad π pomocí Monte Carlo."""
    x = np.random.uniform(-1, 1, n_vzorku)
    y = np.random.uniform(-1, 1, n_vzorku)
    uvnitr = np.sum(x**2 + y**2 <= 1)
    return 4 * uvnitr / n_vzorku

np.random.seed(42)
print(f"Odhad π: {odhadni_pi(100000):.4f}")  # ~3.14

29.5.2 Kapitola 15: Náhodné veličiny

Cvičení 1: Střední hodnota a rozptyl

import numpy as np

data = [2, 4, 4, 4, 5, 5, 7, 9]

stredni = np.mean(data)
rozptyl = np.var(data)
smerodatna = np.std(data)

print(f"Střední hodnota: {stredni}")     # 5.0
print(f"Rozptyl: {rozptyl}")             # 4.0
print(f"Směrodatná odchylka: {smerodatna}")  # 2.0

Cvičení 2: Softmax

def softmax(x):
    """Softmax funkce."""
    exp_x = np.exp(x - np.max(x))  # Numerická stabilita
    return exp_x / np.sum(exp_x)

logits = np.array([2.0, 1.0, 0.1])
probs = softmax(logits)
print(f"Pravděpodobnosti: {probs}")
print(f"Součet: {np.sum(probs)}")  # 1.0

29.5.3 Kapitola 16: Normální rozdělení

Cvičení 1: Z-skóre

def z_skore(x, mu, sigma):
    """Převod na z-skóre."""
    return (x - mu) / sigma

# IQ má N(100, 15^2)
mu, sigma = 100, 15
iq = 130

z = z_skore(iq, mu, sigma)
print(f"Z-skóre pro IQ {iq}: {z:.2f}")  # 2.0

Cvičení 2: Xavier inicializace

import numpy as np

def xavier_init(n_in, n_out):
    """Xavier inicializace vah."""
    std = np.sqrt(2.0 / (n_in + n_out))
    return np.random.randn(n_in, n_out) * std

W = xavier_init(784, 128)
print(f"Střední hodnota: {np.mean(W):.6f}")  # ~0
print(f"Směrodatná odchylka: {np.std(W):.6f}")  # ~0.047

29.5.4 Kapitola 17: Informace a entropie

Cvičení 1: Entropie

def entropie(p):
    """Entropie diskrétního rozdělení."""
    p = np.array(p)
    p = p[p > 0]  # Odstranění nul
    return -np.sum(p * np.log2(p))

# Férová mince
print(f"Entropie mince: {entropie([0.5, 0.5]):.4f} bit")  # 1.0

# Nefér mince
print(f"Entropie nefér: {entropie([0.9, 0.1]):.4f} bit")  # ~0.47

Cvičení 2: Cross-entropy loss

def cross_entropy(y_true, y_pred):
    """Cross-entropy loss."""
    y_pred = np.clip(y_pred, 1e-15, 1 - 1e-15)
    return -np.sum(y_true * np.log(y_pred))

# One-hot encoded: třída 2 (index 1)
y_true = np.array([0, 1, 0])
y_pred = np.array([0.1, 0.8, 0.1])

print(f"Cross-entropy: {cross_entropy(y_true, y_pred):.4f}")  # ~0.223

29.6 Část V: Optimalizace

29.6.1 Kapitola 18: Hledání minima

Cvičení 1: Nalezení minima paraboly

# f(x) = x^2 - 4x + 3
# f'(x) = 2x - 4 = 0 => x = 2

def f(x):
    return x**2 - 4*x + 3

x_min = 2
print(f"Minimum v x = {x_min}")
print(f"f({x_min}) = {f(x_min)}")  # -1

Cvičení 2: Konvexnost

# Funkce f(x) = x^4 - 2x^2
# f''(x) = 12x^2 - 4
# f''(x) < 0 pro |x| < 1/√3, tedy není globálně konvexní

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-2, 2, 100)
f = x**4 - 2*x**2
f_druhá = 12*x**2 - 4

plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.plot(x, f)
plt.title('f(x) = x^4 - 2x^2')
plt.grid(True)

plt.subplot(1, 2, 2)
plt.plot(x, f_druhá)
plt.axhline(y=0, color='r', linestyle='--')
plt.title("f''(x) = 12x^2 - 4")
plt.grid(True)

plt.tight_layout()
plt.show()

29.6.2 Kapitola 19: Gradient descent

Cvičení 1: Implementace GD

def gradient_descent(f, grad_f, x0, lr=0.1, n_iter=100):
    """Gradient descent."""
    x = x0
    history = [x]

    for _ in range(n_iter):
        x = x - lr * grad_f(x)
        history.append(x)

    return x, history

# f(x) = x^2, grad = 2x
f = lambda x: x**2
grad_f = lambda x: 2*x

x_min, history = gradient_descent(f, grad_f, x0=5.0, lr=0.1)
print(f"Minimum: {x_min:.6f}")  # ~0

Cvičení 2: Vliv learning rate

for lr in [0.01, 0.1, 0.5, 0.9]:
    x_min, history = gradient_descent(f, grad_f, x0=5.0, lr=lr, n_iter=20)
    print(f"LR={lr}: x={x_min:.6f}, kroků={len(history)}")

29.6.3 Kapitola 20: Pokročilé optimalizátory

Cvičení 1: Momentum

def gd_momentum(grad_f, x0, lr=0.1, momentum=0.9, n_iter=100):
    """GD s momentum."""
    x = x0
    v = 0  # Rychlost

    for _ in range(n_iter):
        v = momentum * v - lr * grad_f(x)
        x = x + v

    return x

x_min = gd_momentum(lambda x: 2*x, x0=5.0)
print(f"Minimum s momentum: {x_min:.6f}")

Cvičení 2: Adam

def adam(grad_f, x0, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8, n_iter=1000):
    """Adam optimizer."""
    x = x0
    m = 0  # První moment
    v = 0  # Druhý moment

    for t in range(1, n_iter + 1):
        g = grad_f(x)
        m = beta1 * m + (1 - beta1) * g
        v = beta2 * v + (1 - beta2) * g**2

        m_hat = m / (1 - beta1**t)
        v_hat = v / (1 - beta2**t)

        x = x - lr * m_hat / (np.sqrt(v_hat) + eps)

    return x

x_min = adam(lambda x: 2*x, x0=5.0)
print(f"Minimum s Adam: {x_min:.6f}")

29.7 Část VI: Neuronové sítě

29.7.1 Kapitola 21: Perceptron

Cvičení 1: Aktivační funkce

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def relu(x):
    return np.maximum(0, x)

def tanh(x):
    return np.tanh(x)

x = np.linspace(-5, 5, 100)
print(f"sigmoid(0) = {sigmoid(0)}")  # 0.5
print(f"relu(-1) = {relu(-1)}")      # 0
print(f"tanh(0) = {tanh(0)}")        # 0

Cvičení 2: Rozhodovací hranice

# Perceptron: y = sign(w1*x1 + w2*x2 + b)
# Rozhodovací hranice: w1*x1 + w2*x2 + b = 0
# x2 = -(w1*x1 + b) / w2

w1, w2, b = 2, -1, 1

x1 = np.linspace(-3, 3, 100)
x2 = -(w1*x1 + b) / w2

# Hranice je přímka

29.7.2 Kapitola 22: Vícevrstvé sítě

Cvičení 1: Forward pass

import numpy as np

def forward(x, W1, b1, W2, b2):
    """Forward pass pro 2-vrstvou síť."""
    z1 = x @ W1 + b1
    h = np.maximum(0, z1)  # ReLU
    z2 = h @ W2 + b2
    return z2

# Příklad
np.random.seed(42)
x = np.random.randn(1, 4)
W1 = np.random.randn(4, 8)
b1 = np.zeros(8)
W2 = np.random.randn(8, 2)
b2 = np.zeros(2)

output = forward(x, W1, b1, W2, b2)
print(f"Výstup: {output}")

Cvičení 2: Počet parametrů

def pocet_parametru(vrstvy):
    """Spočítá počet parametrů sítě."""
    total = 0
    for i in range(len(vrstvy) - 1):
        n_in, n_out = vrstvy[i], vrstvy[i+1]
        total += n_in * n_out + n_out  # váhy + bias
    return total

# Síť: 784 -> 256 -> 128 -> 10
vrstvy = [784, 256, 128, 10]
print(f"Počet parametrů: {pocet_parametru(vrstvy):,}")
# 784*256 + 256 + 256*128 + 128 + 128*10 + 10 = 235,146

29.7.3 Kapitola 23: Backpropagation

Cvičení 1: Gradient pro MSE

# MSE = (1/n) * sum((y - y_pred)^2)
# d(MSE)/d(y_pred) = -(2/n) * (y - y_pred)

def mse_gradient(y_true, y_pred):
    n = len(y_true)
    return -2/n * (y_true - y_pred)

y_true = np.array([1, 0, 1])
y_pred = np.array([0.9, 0.1, 0.8])

grad = mse_gradient(y_true, y_pred)
print(f"Gradient: {grad}")

Cvičení 2: Backprop pro ReLU

def relu(x):
    return np.maximum(0, x)

def relu_grad(x):
    return (x > 0).astype(float)

# Příklad
x = np.array([-2, -1, 0, 1, 2])
print(f"ReLU({x}) = {relu(x)}")
print(f"ReLU'({x}) = {relu_grad(x)}")

29.7.4 Kapitola 24: Úvod do transformerů

Cvičení 1: Attention

def scaled_dot_attention(Q, K, V):
    """Scaled dot-product attention."""
    d_k = Q.shape[-1]
    scores = Q @ K.T / np.sqrt(d_k)
    weights = np.exp(scores - np.max(scores, axis=-1, keepdims=True))
    weights = weights / weights.sum(axis=-1, keepdims=True)
    return weights @ V

Q = np.array([[1, 0, 1]])
K = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
V = np.array([[1], [2], [3]])

output = scaled_dot_attention(Q, K, V)
print(f"Attention output: {output}")

Cvičení 2: Pozicové kódování

def positional_encoding(seq_len, d_model):
    """Sinusoidální pozicové kódování."""
    PE = np.zeros((seq_len, d_model))
    position = np.arange(seq_len)[:, np.newaxis]
    div_term = np.exp(np.arange(0, d_model, 2) * (-np.log(10000.0) / d_model))

    PE[:, 0::2] = np.sin(position * div_term)
    PE[:, 1::2] = np.cos(position * div_term)

    return PE

PE = positional_encoding(10, 64)
print(f"PE shape: {PE.shape}")
print(f"PE[0, :4]: {PE[0, :4]}")

29.8 Tipy pro řešení cvičení

  1. Začněte jednoduše: Nejprve vyřešte úlohu na papíře nebo pro malý příklad.

  2. Testujte průběžně: Ověřujte řešení na známých případech.

  3. Vizualizujte: Kreslení grafů pomáhá pochopit problém.

  4. Používejte NumPy: Vektorové operace jsou rychlejší a čitelnější.

  5. Čtěte chybové hlášky: Python obvykle říká, co je špatně.

  6. Experimentujte: Měňte parametry a pozorujte změny.