> “A matemática é a linguagem com a qual Deus escreveu o universo.” — Galileu Galilei
Prefácio
Quando um desenvolvedor escreve código, raramente pensa que está aplicando séculos de matematica condensada em poucas linhas. Mas toda vez que um jogo renderiza uma sombra, um algoritmo de busca ordena resultados, uma rede neural aprende ou um sistema criptografado protege dados bancários, equações famosas estão trabalhando silenciosamente por baixo dos panos.
Este artigo é uma jornada pelas equações mais influentes da matemática e da física — e como elas se traduzem diretamente em software do mundo real. Não é um texto acadêmico: é um guia prático para desenvolvedores que querem entender por que o código funciona, não apenas como escrevê-lo.
Cobriremos álgebra linear, cálculo, probabilidade, geometria, teoria dos grafos, física e muito mais — sempre com exemplos de código, casos de uso reais e aplicações modernas.
Parte I — Álgebra e Aritmética Fundamental
Capítulo 1 — A Equação de Euler: e^(iπ) + 1 = 0
Considerada por muitos a equação mais bela da matemática, a identidade de Euler conecta cinco das constantes mais importantes:
e(número de Euler, base do logaritmo natural ≈ 2,71828)i(unidade imaginária, √-1)π(pi ≈ 3,14159)1(identidade multiplicativa)0(identidade aditiva)
#### A Fórmula Completa de Euler
A identidade é um caso especial da fórmula geral:
e^(iθ) = cos(θ) + i·sin(θ)
#### Aplicação 1: Transformada de Fourier (FFT)
A Transformada de Fourier é o coração do processamento de sinais. Ela converte um sinal do domínio do tempo para o domínio da frequência usando exatamente a fórmula de Euler:
F(ω) = ∫ f(t) · e^(-iωt) dt
Em código Python:
import numpy as np
import matplotlib.pyplot as plt
# Criando um sinal composto de múltiplas frequências
def create_signal(duration=1.0, sample_rate=1000):
t = np.linspace(0, duration, int(sample_rate * duration))
# Sinal com frequências de 50Hz, 120Hz e 200Hz
signal = (
np.sin(2 * np.pi * 50 * t) +
0.5 * np.sin(2 * np.pi * 120 * t) +
0.25 * np.sin(2 * np.pi * 200 * t)
)
return t, signal
# Aplicando a FFT (Fast Fourier Transform)
def analyze_frequencies(signal, sample_rate=1000):
n = len(signal)
# A FFT usa a fórmula de Euler internamente
fft_result = np.fft.fft(signal)
# Frequências correspondentes
frequencies = np.fft.fftfreq(n, d=1/sample_rate)
# Magnitude do espectro
magnitude = np.abs(fft_result) / n
return frequencies[:n//2], magnitude[:n//2]
t, signal = create_signal()
freqs, magnitudes = analyze_frequencies(signal)
# Identificar picos de frequência
peak_threshold = 0.1
peaks = freqs[magnitudes > peak_threshold]
print(f"Frequências dominantes detectadas: {peaks} Hz")
# Output: Frequências dominantes detectadas: [50. 120. 200.] Hz
#### Aplicação 2: Números Complexos em Processamento de Imagens
import numpy as np
def apply_fft_image_filter(image_array, cutoff_frequency=30):
"""
Aplica filtro de baixa frequência usando FFT 2D.
Usa e^(iθ) internamente para decomposição.
"""
# FFT 2D da imagem
fft_image = np.fft.fft2(image_array)
fft_shifted = np.fft.fftshift(fft_image)
rows, cols = image_array.shape
center_row, center_col = rows // 2, cols // 2
# Criar máscara circular (filtro passa-baixa)
mask = np.zeros((rows, cols))
for r in range(rows):
for c in range(cols):
dist = np.sqrt((r - center_row)**2 + (c - center_col)**2)
if dist <= cutoff_frequency:
mask[r, c] = 1
# Aplicar filtro no domínio da frequência
filtered_fft = fft_shifted * mask
# Transformada inversa para recuperar imagem
ifft_shifted = np.fft.ifftshift(filtered_fft)
filtered_image = np.real(np.fft.ifft2(ifft_shifted))
return filtered_image
# Uso: suavização de imagens, compressão JPEG, detecção de bordas
#### Aplicação 3: Rotação em Espaço Complexo (Motores 2D)
import cmath
import math
def rotate_point_complex(x, y, angle_degrees):
"""
Rotaciona um ponto usando multiplicação de números complexos.
Baseado em: e^(iθ) = cos(θ) + i·sin(θ)
"""
point = complex(x, y)
angle_rad = math.radians(angle_degrees)
# Fator de rotação usando a fórmula de Euler
rotation_factor = cmath.exp(1j * angle_rad)
rotated = point * rotation_factor
return rotated.real, rotated.imag
# Exemplo em um motor de jogo 2D
player_x, player_y = 100, 0
new_x, new_y = rotate_point_complex(player_x, player_y, 45)
print(f"Posição original: ({player_x}, {player_y})")
print(f"Após rotação de 45°: ({new_x:.2f}, {new_y:.2f})")
# Output: Posição original: (100, 0)
# Após rotação de 45°: (70.71, 70.71)
Capítulo 2 — O Teorema de Pitágoras: a² + b² = c²
Uma das equações mais antigas e mais usadas no software. Sempre que há distância, há Pitágoras.
a² + b² = c²
#### Aplicação 1: Detecção de Colisão em Jogos
import math
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def distance_to(self, other):
"""Pitágoras: √((x2-x1)² + (y2-y1)²)"""
dx = self.x - other.x
dy = self.y - other.y
return math.sqrt(dx**2 + dy**2)
def distance_squared(self, other):
"""Versão otimizada — evita sqrt quando só precisamos comparar"""
dx = self.x - other.x
dy = self.y - other.y
return dx**2 + dy**2
class CircleCollider:
def __init__(self, center: Vector2D, radius: float):
self.center = center
self.radius = radius
def is_colliding(self, other: 'CircleCollider') -> bool:
"""
Colisão ocorre quando:
distância < soma dos raios
√((x2-x1)² + (y2-y1)²) < r1 + r2
Otimização: comparamos quadrados para evitar sqrt
"""
distance_sq = self.center.distance_squared(other.center)
radius_sum = self.radius + other.radius
return distance_sq < radius_sum ** 2
# Teste de colisão
player = CircleCollider(Vector2D(0, 0), radius=50)
enemy = CircleCollider(Vector2D(80, 0), radius=50)
if player.is_colliding(enemy):
print("COLISÃO DETECTADA!")
else:
print(f"Sem colisão. Distância: {player.center.distance_to(enemy.center):.1f}px")
#### Aplicação 2: Pathfinding com Heurística Euclidiana
import heapq
import math
from typing import List, Tuple, Dict
def euclidean_heuristic(a: Tuple, b: Tuple) -> float:
"""
Heurística de distância euclidiana para A*.
Usa Pitágoras: √((x2-x1)² + (y2-y1)²)
"""
return math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2)
def astar(grid: List[List[int]], start: Tuple, goal: Tuple) -> List[Tuple]:
"""
Algoritmo A* com heurística euclidiana baseada em Pitágoras.
"""
open_set = [(0, start)]
came_from = {}
g_score = {start: 0}
f_score = {start: euclidean_heuristic(start, goal)}
while open_set:
current = heapq.heappop(open_set)[1]
if current == goal:
path = []
while current in came_from:
path.append(current)
current = came_from[current]
path.append(start)
return path[::-1]
row, col = current
neighbors = [
(row+1, col), (row-1, col),
(row, col+1), (row, col-1),
(row+1, col+1), (row-1, col-1), # Diagonais
(row+1, col-1), (row-1, col+1)
]
for neighbor in neighbors:
nr, nc = neighbor
if 0 <= nr < len(grid) and 0 <= nc < len(grid[0]):
if grid[nr][nc] == 1: # Obstáculo
continue
# Custo diagonal = √2 ≈ 1.414 (Pitágoras!)
move_cost = 1.414 if abs(nr-row) + abs(nc-col) == 2 else 1.0
tentative_g = g_score[current] + move_cost
if neighbor not in g_score or tentative_g < g_score[neighbor]:
came_from[neighbor] = current
g_score[neighbor] = tentative_g
f = tentative_g + euclidean_heuristic(neighbor, goal)
f_score[neighbor] = f
heapq.heappush(open_set, (f, neighbor))
return [] # Caminho não encontrado
#### Aplicação 3: Geolocalização e Mapas
import math
def haversine_distance(lat1, lon1, lat2, lon2):
"""
Distância entre dois pontos na superfície esférica da Terra.
Extensão esférica do teorema de Pitágoras.
Retorna distância em quilômetros.
"""
R = 6371 # Raio da Terra em km
lat1_rad = math.radians(lat1)
lat2_rad = math.radians(lat2)
delta_lat = math.radians(lat2 - lat1)
delta_lon = math.radians(lon2 - lon1)
# Fórmula de Haversine (derivada do teorema esférico de Pitágoras)
a = (math.sin(delta_lat/2)**2 +
math.cos(lat1_rad) * math.cos(lat2_rad) *
math.sin(delta_lon/2)**2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
distance = R * c
return distance
# Distância entre São Paulo e Rio de Janeiro
sp_lat, sp_lon = -23.5505, -46.6333
rj_lat, rj_lon = -22.9068, -43.1729
dist = haversine_distance(sp_lat, sp_lon, rj_lat, rj_lon)
print(f"Distância SP → RJ: {dist:.1f} km")
# Output: Distância SP → RJ: ~358 km
Capítulo 3 — Logaritmos e Exponenciais
Logaritmos são a espinha dorsal da análise de algoritmos. Sempre que vemos O(log n), há um logaritmo operando.
log_b(x) = y ⟺ b^y = x
#### Aplicação 1: Análise de Complexidade com Árvores Binárias
import math
import time
class BinarySearchTree:
def __init__(self):
self.root = None
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def insert(self, value):
# O(log n) em árvore balanceada
if not self.root:
self.root = self.Node(value)
else:
self._insert_recursive(self.root, value)
def _insert_recursive(self, node, value):
if value < node.value:
if node.left is None:
node.left = self.Node(value)
else:
self._insert_recursive(node.left, value)
else:
if node.right is None:
node.right = self.Node(value)
else:
self._insert_recursive(node.right, value)
def search(self, value):
"""
Busca em O(log n) — a cada passo, metade dos elementos é descartada.
n=1000 → max ~10 passos (log₂(1000) ≈ 9.96)
n=1000000 → max ~20 passos (log₂(1000000) ≈ 19.93)
"""
return self._search_recursive(self.root, value, steps=0)
def _search_recursive(self, node, value, steps):
steps += 1
if node is None:
return None, steps
if node.value == value:
return node.value, steps
if value < node.value:
return self._search_recursive(node.left, value, steps)
return self._search_recursive(node.right, value, steps)
# Demonstração da eficiência logarítmica
bst = BinarySearchTree()
import random
n = 1_000_000
values = random.sample(range(n * 10), n)
for v in values:
bst.insert(v)
target = values[n // 2]
result, steps = bst.search(target)
theoretical_max = math.log2(n)
print(f"n = {n:,}")
print(f"Passos realizados: {steps}")
print(f"Máximo teórico log₂({n}) = {theoretical_max:.1f}")
#### Aplicação 2: Entropia de Shannon em Machine Learning
import math
from collections import Counter
from typing import List
def shannon_entropy(data: List) -> float:
"""
Entropia de Shannon: H(X) = -Σ p(x) · log₂(p(x))
Mede a "incerteza" ou "impureza" de um conjunto.
Usada em árvores de decisão (ID3, C4.5, CART).
"""
if not data:
return 0
n = len(data)
counts = Counter(data)
entropy = 0
for count in counts.values():
probability = count / n
if probability > 0:
entropy -= probability * math.log2(probability)
return entropy
def information_gain(parent_data: List, splits: List[List]) -> float:
"""
Ganho de informação = Entropia(pai) - Σ [|filho|/|pai| · Entropia(filho)]
Principal critério de divisão em árvores de decisão.
"""
parent_entropy = shannon_entropy(parent_data)
n_parent = len(parent_data)
weighted_child_entropy = sum(
(len(child) / n_parent) * shannon_entropy(child)
for child in splits
)
return parent_entropy - weighted_child_entropy
# Exemplo: classificar e-mails como spam ou não-spam
emails_labels = ['spam', 'spam', 'spam', 'ham', 'ham', 'spam', 'ham', 'ham']
print(f"Entropia do conjunto: {shannon_entropy(emails_labels):.4f} bits")
# Split por uma feature: "contém palavra 'grátis'"
with_free = ['spam', 'spam', 'spam', 'spam']
without_free = ['ham', 'ham', 'ham', 'ham']
gain = information_gain(emails_labels, [with_free, without_free])
print(f"Ganho de informação da feature 'grátis': {gain:.4f} bits")
# Quanto maior, melhor o split!
#### Aplicação 3: Escala dB em Processamento de Áudio
import math
def amplitude_to_db(amplitude: float) -> float:
"""
Conversão de amplitude para decibéis.
dB = 20 · log₁₀(amplitude)
Usada em equalizadores, compressores de áudio, DAWs.
"""
if amplitude <= 0:
return float('-inf')
return 20 * math.log10(amplitude)
def db_to_amplitude(db: float) -> float:
"""
Conversão inversa: amplitude = 10^(dB/20)
"""
return 10 ** (db / 20)
class AudioCompressor:
def __init__(self, threshold_db=-20, ratio=4.0, attack_ms=10, release_ms=100):
self.threshold = db_to_amplitude(threshold_db)
self.ratio = ratio
self.attack = attack_ms / 1000
self.release = release_ms / 1000
def process_sample(self, sample: float) -> float:
"""
Aplica compressão dinâmica usando escala logarítmica.
Simula comportamento perceptivo humano (escala log).
"""
level = abs(sample)
if level > self.threshold:
# Redução logarítmica acima do threshold
excess_db = amplitude_to_db(level) - amplitude_to_db(self.threshold)
gain_reduction_db = excess_db * (1 - 1/self.ratio)
gain = db_to_amplitude(-gain_reduction_db)
return sample * gain
return sample
# Simulação
compressor = AudioCompressor(threshold_db=-20, ratio=4.0)
test_amplitudes = [0.01, 0.1, 0.5, 1.0, 2.0]
print("Amplitude | dB | Após compressão")
print("-" * 40)
for amp in test_amplitudes:
db = amplitude_to_db(amp)
compressed = compressor.process_sample(amp)
print(f" {amp:.2f} | {db:6.1f} | {compressed:.4f}")
Parte II — Álgebra Linear
Capítulo 4 — Transformações Matriciais
Matrizes são a linguagem universal do software gráfico. Todo pixel na sua tela, toda animação 3D, toda rede neural — tudo passa por matrizes.
[resultado] = [matriz de transformação] × [vetor de entrada]
#### Aplicação 1: Transformações 3D em OpenGL/WebGL
import numpy as np
def translation_matrix(tx, ty, tz):
"""Matriz de translação 4x4 (coordenadas homogêneas)"""
return np.array([
[1, 0, 0, tx],
[0, 1, 0, ty],
[0, 0, 1, tz],
[0, 0, 0, 1]
], dtype=float)
def rotation_matrix_y(angle_deg):
"""Rotação em torno do eixo Y"""
angle = np.radians(angle_deg)
cos_a, sin_a = np.cos(angle), np.sin(angle)
return np.array([
[ cos_a, 0, sin_a, 0],
[ 0, 1, 0, 0],
[-sin_a, 0, cos_a, 0],
[ 0, 0, 0, 1]
], dtype=float)
def scale_matrix(sx, sy, sz):
"""Matriz de escala"""
return np.array([
[sx, 0, 0, 0],
[ 0, sy, 0, 0],
[ 0, 0, sz, 0],
[ 0, 0, 0, 1]
], dtype=float)
def perspective_projection(fov_deg, aspect, near, far):
"""
Matriz de projeção perspectiva.
A equação fundamental que transforma 3D → 2D com perspectiva.
"""
fov = np.radians(fov_deg)
f = 1.0 / np.tan(fov / 2)
return np.array([
[f/aspect, 0, 0, 0],
[ 0, f, 0, 0],
[ 0, 0, (far+near)/(near-far), (2*far*near)/(near-far)],
[ 0, 0, -1, 0]
], dtype=float)
# Simulação de pipeline de renderização 3D
def transform_vertex(vertex_3d, model_matrix, view_matrix, proj_matrix):
"""
Pipeline MVP (Model-View-Projection):
clip_space = Projection × View × Model × vertex
"""
# Converter para coordenadas homogêneas
vertex_4d = np.append(vertex_3d, 1.0)
# Aplicar transformações em sequência (multiplicação matricial)
world_space = model_matrix @ vertex_4d
camera_space = view_matrix @ world_space
clip_space = proj_matrix @ camera_space
# Divisão perspectiva (W-division)
if clip_space[3] != 0:
ndc = clip_space[:3] / clip_space[3]
else:
ndc = clip_space[:3]
return ndc
# Exemplo prático
vertex = np.array([1.0, 0.0, -3.0])
# Modelo rotacionado 45° e translacionado
model = translation_matrix(0, 0, -5) @ rotation_matrix_y(45)
# View matrix (câmera na origem olhando para -Z)
view = np.eye(4)
# Projeção perspectiva
projection = perspective_projection(fov_deg=60, aspect=16/9, near=0.1, far=100)
ndc = transform_vertex(vertex, model, view, projection)
print(f"Vértice 3D: {vertex}")
print(f"NDC (Normalized Device Coordinates): {ndc}")
#### Aplicação 2: Redes Neurais — Forward Pass
import numpy as np
def sigmoid(x):
"""σ(x) = 1 / (1 + e^(-x))"""
return 1 / (1 + np.exp(-x))
def relu(x):
"""ReLU(x) = max(0, x)"""
return np.maximum(0, x)
class NeuralLayer:
def __init__(self, input_size, output_size, activation='relu'):
# Inicialização de He (para ReLU)
self.weights = np.random.randn(output_size, input_size) * np.sqrt(2/input_size)
self.biases = np.zeros(output_size)
self.activation_fn = relu if activation == 'relu' else sigmoid
def forward(self, x):
"""
Forward pass: z = W·x + b
a = activation(z)
Multiplicação matricial W·x é a operação fundamental.
"""
self.input = x
self.z = self.weights @ x + self.biases
self.output = self.activation_fn(self.z)
return self.output
class NeuralNetwork:
def __init__(self, layer_sizes):
self.layers = []
for i in range(len(layer_sizes) - 1):
activation = 'relu' if i < len(layer_sizes) - 2 else 'sigmoid'
self.layers.append(NeuralLayer(layer_sizes[i], layer_sizes[i+1], activation))
def predict(self, x):
"""
Propagação forward através de todas as camadas.
Essencialmente: y = f_n(W_n · f_(n-1)(W_(n-1) · ... · f_1(W_1·x + b_1)...))
"""
current = x
for layer in self.layers:
current = layer.forward(current)
return current
# Rede neural para classificação binária
# Arquitetura: 784 → 256 → 128 → 1
network = NeuralNetwork([784, 256, 128, 1])
# Simular uma imagem 28x28 de dígito manuscrito
sample_image = np.random.rand(784)
prediction = network.predict(sample_image)
print(f"Predição da rede neural: {prediction[0]:.4f}")
print(f"Classificação: {'Positivo' if prediction[0] > 0.5 else 'Negativo'}")
Capítulo 5 — Decomposição em Valores Singulares (SVD)
A SVD é talvez a fatoração matricial mais poderosa da engenharia de software moderna.
A = U · Σ · V^T
#### Aplicação 1: Sistema de Recomendação (Netflix, Spotify)
import numpy as np
def collaborative_filtering_svd(ratings_matrix, n_factors=10):
"""
Sistema de recomendação baseado em SVD.
Princípio: decompor a matriz de avaliações em fatores latentes.
ratings_matrix[user, item] = nota do usuário para o item
"""
# Preencher valores faltantes com a média de cada usuário
user_means = np.nanmean(ratings_matrix, axis=1, keepdims=True)
normalized = np.where(np.isnan(ratings_matrix), 0, ratings_matrix - user_means)
# Decomposição SVD: A = U · Σ · V^T
U, sigma, Vt = np.linalg.svd(normalized, full_matrices=False)
# Reduzir para k fatores latentes (compressão)
U_k = U[:, :n_factors]
sigma_k = np.diag(sigma[:n_factors])
Vt_k = Vt[:n_factors, :]
# Reconstruir matriz aproximada
reconstructed = U_k @ sigma_k @ Vt_k + user_means
return reconstructed, U_k, sigma_k, Vt_k
def get_recommendations(user_id, ratings_matrix, n_recommendations=5):
"""
Recomendar itens não avaliados para um usuário.
"""
predicted_matrix, _, _, _ = collaborative_filtering_svd(ratings_matrix)
user_ratings = ratings_matrix[user_id]
predicted_ratings = predicted_matrix[user_id]
# Mascarar itens já avaliados
unrated_mask = np.isnan(user_ratings)
predicted_unrated = np.where(unrated_mask, predicted_ratings, -np.inf)
# Top N recomendações
top_items = np.argsort(predicted_unrated)[::-1][:n_recommendations]
return top_items, predicted_unrated[top_items]
# Matriz de avaliações: usuários × filmes (NaN = não assistido)
ratings = np.array([
[5, 4, np.nan, 1, 2],
[4, np.nan, 4, 1, np.nan],
[np.nan, 3, 5, np.nan, 4],
[1, 2, np.nan, 5, 4],
[np.nan, 1, 2, 4, 5]
])
user = 0
recs, scores = get_recommendations(user, ratings)
print(f"Recomendações para usuário {user}:")
for item, score in zip(recs, scores):
print(f" Filme {item}: score previsto = {score:.2f}")
#### Aplicação 2: Compressão de Imagens com SVD
import numpy as np
def compress_image_svd(image_channel, k_components):
"""
Comprime um canal de imagem usando SVD.
Mantém apenas os k maiores valores singulares.
Quanto menor k, maior a compressão (e menor a qualidade).
"""
# SVD da matriz de pixels
U, sigma, Vt = np.linalg.svd(image_channel, full_matrices=False)
# Manter apenas k componentes
U_k = U[:, :k_components]
sigma_k = sigma[:k_components]
Vt_k = Vt[:k_components, :]
# Reconstruir imagem comprimida
compressed = U_k @ np.diag(sigma_k) @ Vt_k
# Calcular taxa de compressão
original_size = image_channel.size
compressed_size = U_k.size + sigma_k.size + Vt_k.size
compression_ratio = original_size / compressed_size
# Calcular erro (RMSE)
rmse = np.sqrt(np.mean((image_channel - compressed)**2))
return compressed, compression_ratio, rmse
# Simulação com imagem aleatória 256x256
np.random.seed(42)
image = np.random.rand(256, 256) * 255
for k in [5, 20, 50, 100]:
compressed, ratio, error = compress_image_svd(image, k)
print(f"k={k:3d}: taxa de compressão={ratio:.1f}x, RMSE={error:.2f}")
# k= 5: taxa de compressão=..., alta compressão, baixa qualidade
# k=100: taxa de compressão=..., compressão moderada, boa qualidade
Parte III — Cálculo e Otimização
Capítulo 6 — Gradiente Descendente: θ = θ - α·∇J(θ)
A equação que treina toda rede neural do mundo. O gradiente descendente é o algoritmo de otimização mais importante do machine learning moderno.
θ_(t+1) = θ_t - α · ∇J(θ_t)
Onde:
θ= parâmetros (pesos da rede)α= taxa de aprendizado (learning rate)∇J(θ)= gradiente da função de custo
#### Aplicação 1: Regressão Linear com Gradiente Descendente
import numpy as np
import matplotlib.pyplot as plt
class LinearRegression:
def __init__(self, learning_rate=0.01, n_iterations=1000):
self.lr = learning_rate
self.n_iter = n_iterations
self.weights = None
self.bias = None
self.cost_history = []
def _mean_squared_error(self, y_true, y_pred):
"""J(w,b) = (1/2m) · Σ(y_pred - y_true)²"""
m = len(y_true)
return (1/(2*m)) * np.sum((y_pred - y_true)**2)
def fit(self, X, y):
m, n = X.shape
self.weights = np.zeros(n)
self.bias = 0
for iteration in range(self.n_iter):
# Forward pass: ŷ = X·w + b
y_pred = X @ self.weights + self.bias
# Calcular custo
cost = self._mean_squared_error(y, y_pred)
self.cost_history.append(cost)
# Calcular gradientes (derivadas parciais)
# ∂J/∂w = (1/m) · X^T · (ŷ - y)
# ∂J/∂b = (1/m) · Σ(ŷ - y)
m = len(y)
dw = (1/m) * X.T @ (y_pred - y)
db = (1/m) * np.sum(y_pred - y)
# Atualização dos parâmetros: θ = θ - α·∇J(θ)
self.weights -= self.lr * dw
self.bias -= self.lr * db
if iteration % 100 == 0:
print(f"Iteração {iteration:4d} | Custo: {cost:.6f}")
return self
def predict(self, X):
return X @ self.weights + self.bias
# Dataset: prever preço de casa pelo tamanho
np.random.seed(42)
n_samples = 200
X = np.random.uniform(50, 300, n_samples).reshape(-1, 1)
y = 1500 * X[:, 0] + 50000 + np.random.normal(0, 15000, n_samples)
# Normalização (importante para convergência do gradiente)
X_normalized = (X - X.mean()) / X.std()
model = LinearRegression(learning_rate=0.1, n_iterations=500)
model.fit(X_normalized, y)
# Previsão
area_nova = np.array([[120]])
area_normalizada = (area_nova - X.mean()) / X.std()
preco_previsto = model.predict(area_normalizada)
print(f"nPreço previsto para 120m²: R$ {preco_previsto[0]:,.0f}")
#### Aplicação 2: Adam Optimizer (Estado da Arte)
import numpy as np
class AdamOptimizer:
"""
Adam = Adaptive Moment Estimation
m_t = β₁·m_(t-1) + (1-β₁)·g_t (primeiro momento - média)
v_t = β₂·v_(t-1) + (1-β₂)·g_t² (segundo momento - variância)
m̂_t = m_t / (1-β₁^t) (correção de viés)
v̂_t = v_t / (1-β₂^t) (correção de viés)
θ_t = θ_(t-1) - α · m̂_t / (√v̂_t + ε) (atualização)
"""
def __init__(self, params, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
self.params = params
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.eps = eps
self.t = 0
# Inicializar momentos zerados
self.m = {k: np.zeros_like(v) for k, v in params.items()}
self.v = {k: np.zeros_like(v) for k, v in params.items()}
def step(self, grads):
self.t += 1
updated_params = {}
for key in self.params:
g = grads[key]
# Atualizar momentos
self.m[key] = self.beta1 * self.m[key] + (1 - self.beta1) * g
self.v[key] = self.beta2 * self.v[key] + (1 - self.beta2) * g**2
# Correção de viés
m_hat = self.m[key] / (1 - self.beta1**self.t)
v_hat = self.v[key] / (1 - self.beta2**self.t)
# Atualização de parâmetros
updated_params[key] = (
self.params[key] - self.lr * m_hat / (np.sqrt(v_hat) + self.eps)
)
return updated_params
# Comparação: SGD vs Adam em uma superfície de custo complexa
def rosenbrock(x, y):
"""Função de Rosenbrock: f(x,y) = (1-x)² + 100(y-x²)²"""
return (1-x)**2 + 100*(y - x**2)**2
def rosenbrock_grad(x, y):
dx = -2*(1-x) - 400*x*(y - x**2)
dy = 200*(y - x**2)
return dx, dy
# SGD simples
x_sgd, y_sgd = -1.0, 1.0
lr = 0.001
for _ in range(1000):
gx, gy = rosenbrock_grad(x_sgd, y_sgd)
x_sgd -= lr * gx
y_sgd -= lr * gy
# Adam
x_adam, y_adam = -1.0, 1.0
params = {'x': np.array(x_adam), 'y': np.array(y_adam)}
optimizer = AdamOptimizer(params)
for _ in range(1000):
gx, gy = rosenbrock_grad(params['x'], params['y'])
grads = {'x': np.array(gx), 'y': np.array(gy)}
params = optimizer.step(grads)
print(f"Mínimo em (1, 1)")
print(f"SGD chegou em: ({x_sgd:.4f}, {y_sgd:.4f})")
print(f"Adam chegou em: ({params['x']:.4f}, {params['y']:.4f})")
Capítulo 7 — A Equação de Bayes: P(A|B) = P(B|A)·P(A) / P(B)
O Teorema de Bayes é o fundamento do raciocínio probabilístico em software.
#### Aplicação 1: Filtro de Spam Bayesiano
import math
from collections import defaultdict
class NaiveBayesClassifier:
"""
Classificador Naive Bayes para detecção de spam.
P(spam | palavras) ∝ P(palavras | spam) · P(spam)
P(ham | palavras) ∝ P(palavras | ham) · P(ham)
"Naive" = assume independência entre palavras.
"""
def __init__(self, alpha=1.0): # alpha = suavização de Laplace
self.alpha = alpha
self.class_probs = {}
self.word_probs = defaultdict(dict)
self.vocabulary = set()
def fit(self, documents, labels):
"""Treinar com Teorema de Bayes."""
n_total = len(labels)
classes = set(labels)
# P(classe) = count(classe) / total
class_counts = defaultdict(int)
for label in labels:
class_counts[label] += 1
self.class_probs = {
c: count / n_total
for c, count in class_counts.items()
}
# Contar palavras por classe
word_counts = {c: defaultdict(int) for c in classes}
total_words = {c: 0 for c in classes}
for doc, label in zip(documents, labels):
words = doc.lower().split()
for word in words:
self.vocabulary.add(word)
word_counts[label][word] += 1
total_words[label] += 1
# P(palavra | classe) com suavização de Laplace
vocab_size = len(self.vocabulary)
for c in classes:
for word in self.vocabulary:
count = word_counts[c][word]
# Laplace smoothing: evita probabilidade zero
self.word_probs[c][word] = (
(count + self.alpha) /
(total_words[c] + self.alpha * vocab_size)
)
def predict(self, document):
words = document.lower().split()
best_class = None
best_log_prob = float('-inf')
for c, prior in self.class_probs.items():
# Usar log-probabilidades para evitar underflow numérico
log_prob = math.log(prior)
for word in words:
if word in self.word_probs[c]:
log_prob += math.log(self.word_probs[c][word])
if log_prob > best_log_prob:
best_log_prob = log_prob
best_class = c
return best_class
# Treinar e testar
spam_samples = [
("grátis ganhe dinheiro agora clique aqui oferta", "spam"),
("promoção especial compre agora desconto incrível", "spam"),
("você ganhou prêmio resgate já clique link", "spam"),
("reunião amanhã às 10h sala conferência pauta", "ham"),
("relatório mensal vendas anexo revisão necessária", "ham"),
("almoço sexta-feira restaurante convite amigos", "ham"),
]
docs, labels = zip(*spam_samples)
classifier = NaiveBayesClassifier()
classifier.fit(docs, labels)
test_messages = [
"clique aqui ganhe prêmio grátis especial",
"reunião revisão relatório trimestral amanhã",
"oferta incrível dinheiro fácil agora"
]
for msg in test_messages:
result = classifier.predict(msg)
print(f"'{msg[:40]}...' → {result.upper()}")
Parte IV — Teoria dos Grafos
Capítulo 8 — Algoritmo de Dijkstra
O algoritmo de Dijkstra encontra o caminho mais curto em grafos ponderados. É usado em GPS, roteamento de redes, jogos e muito mais.
d[v] = min(d[v], d[u] + w(u,v))
#### Aplicação 1: Roteamento GPS
import heapq
from typing import Dict, List, Tuple, Optional
class WeightedGraph:
def __init__(self):
self.adjacency_list: Dict[str, List[Tuple[str, float]]] = {}
def add_edge(self, from_node, to_node, weight, bidirectional=True):
if from_node not in self.adjacency_list:
self.adjacency_list[from_node] = []
if to_node not in self.adjacency_list:
self.adjacency_list[to_node] = []
self.adjacency_list[from_node].append((to_node, weight))
if bidirectional:
self.adjacency_list[to_node].append((from_node, weight))
def dijkstra(self, start: str, end: Optional[str] = None):
"""
Algoritmo de Dijkstra.
Invariante: d[v] = min(d[v], d[u] + w(u,v))
Complexidade: O((V + E) · log V)
"""
distances = {node: float('inf') for node in self.adjacency_list}
distances[start] = 0
predecessors = {node: None for node in self.adjacency_list}
# Fila de prioridade: (distância, nó)
priority_queue = [(0, start)]
visited = set()
while priority_queue:
current_dist, current_node = heapq.heappop(priority_queue)
if current_node in visited:
continue
visited.add(current_node)
if end and current_node == end:
break
for neighbor, weight in self.adjacency_list.get(current_node, []):
if neighbor in visited:
continue
# Relaxamento da aresta
new_dist = current_dist + weight
if new_dist < distances[neighbor]:
distances[neighbor] = new_dist
predecessors[neighbor] = current_node
heapq.heappush(priority_queue, (new_dist, neighbor))
return distances, predecessors
def shortest_path(self, start: str, end: str):
"""Recuperar o caminho mais curto."""
distances, predecessors = self.dijkstra(start, end)
if distances[end] == float('inf'):
return None, float('inf')
path = []
current = end
while current is not None:
path.append(current)
current = predecessors[current]
return path[::-1], distances[end]
# Mapa de ruas de uma cidade
city_map = WeightedGraph()
streets = [
("Centro", "Paulista", 2.1),
("Centro", "Brás", 3.4),
("Paulista", "Jardins", 1.8),
("Paulista", "Pinheiros", 4.2),
("Brás", "Mooca", 2.7),
("Jardins", "Pinheiros", 2.3),
("Pinheiros", "Vila Madalena", 1.5),
("Vila Madalena", "Lapa", 3.1),
("Mooca", "Tatuapé", 2.9),
("Lapa", "Centro", 5.8),
]
for from_, to_, dist in streets:
city_map.add_edge(from_, to_, dist)
# Encontrar rota mais rápida
origin, destination = "Brás", "Vila Madalena"
path, total_distance = city_map.shortest_path(origin, destination)
print(f"Rota mais curta: {origin} → {destination}")
print(f"Caminho: {' → '.join(path)}")
print(f"Distância total: {total_distance:.1f} km")
#### Aplicação 2: Roteamento de Redes (OSPF)
import heapq
from dataclasses import dataclass
from typing import List, Optional
@dataclass
class NetworkNode:
name: str
ip: str
@dataclass
class NetworkLink:
from_node: str
to_node: str
bandwidth_mbps: float
latency_ms: float
@property
def cost(self):
"""
Custo OSPF baseado em largura de banda.
RFC 2328: custo = 100Mbps / bandwidth
"""
return 100 / self.bandwidth_mbps + self.latency_ms / 10
class OSPFRouter:
def __init__(self):
self.nodes = {}
self.links = []
self.routing_table = {}
def add_node(self, node: NetworkNode):
self.nodes[node.name] = node
def add_link(self, link: NetworkLink):
self.links.append(link)
def compute_routing_table(self, source: str):
"""Calcula tabela de roteamento usando Dijkstra."""
graph = {}
for link in self.links:
if link.from_node not in graph:
graph[link.from_node] = []
if link.to_node not in graph:
graph[link.to_node] = []
graph[link.from_node].append((link.to_node, link.cost))
graph[link.to_node].append((link.from_node, link.cost))
distances = {n: float('inf') for n in self.nodes}
distances[source] = 0
next_hop = {n: None for n in self.nodes}
pq = [(0, source, None)]
visited = set()
while pq:
cost, node, hop = heapq.heappop(pq)
if node in visited:
continue
visited.add(node)
if next_hop[node] is None and node != source:
next_hop[node] = hop
for neighbor, link_cost in graph.get(node, []):
if neighbor not in visited:
new_cost = cost + link_cost
if new_cost < distances[neighbor]:
distances[neighbor] = new_cost
first_hop = hop if hop else neighbor
next_hop[neighbor] = first_hop if node == source else next_hop[node]
heapq.heappush(pq, (new_cost, neighbor, first_hop))
self.routing_table[source] = {
dest: {"next_hop": nh, "cost": distances[dest]}
for dest, nh in next_hop.items()
if dest != source
}
return self.routing_table[source]
# Topologia de rede corporativa
router = OSPFRouter()
nodes = [
NetworkNode("Core-SW", "10.0.0.1"),
NetworkNode("Dist-A", "10.0.1.1"),
NetworkNode("Dist-B", "10.0.2.1"),
NetworkNode("Access-1", "10.1.0.1"),
NetworkNode("Access-2", "10.2.0.1"),
NetworkNode("Access-3", "10.3.0.1"),
]
for node in nodes:
router.add_node(node)
links = [
NetworkLink("Core-SW", "Dist-A", bandwidth_mbps=10000, latency_ms=0.1),
NetworkLink("Core-SW", "Dist-B", bandwidth_mbps=10000, latency_ms=0.1),
NetworkLink("Dist-A", "Access-1", bandwidth_mbps=1000, latency_ms=0.5),
NetworkLink("Dist-A", "Access-2", bandwidth_mbps=1000, latency_ms=0.5),
NetworkLink("Dist-B", "Access-3", bandwidth_mbps=1000, latency_ms=0.5),
]
for link in links:
router.add_link(link)
table = router.compute_routing_table("Core-SW")
print("Tabela de roteamento do Core-SW:")
for dest, info in table.items():
print(f" → {dest}: via {info['next_hop']} (custo: {info['cost']:.3f})")
Parte V — Probabilidade e Estatística
Capítulo 9 — Distribuição Normal: f(x) = (1/σ√2π) · e^(-(x-μ)²/2σ²)
A distribuição normal (gaussiana) aparece em testes A/B, geração procedural, simulações e machine learning.
#### Aplicação 1: Teste A/B Estatístico
import math
import random
from typing import List, Tuple
def normal_pdf(x: float, mu: float, sigma: float) -> float:
"""
Função densidade de probabilidade normal.
f(x) = (1/σ√2π) · e^(-(x-μ)²/2σ²)
"""
coefficient = 1 / (sigma * math.sqrt(2 * math.pi))
exponent = -((x - mu)**2) / (2 * sigma**2)
return coefficient * math.exp(exponent)
def error_function(x: float) -> float:
"""
Função de erro (erf) — aproximação de Abramowitz & Stegun.
Usada para calcular probabilidades acumuladas.
"""
t = 1 / (1 + 0.3275911 * abs(x))
poly = t * (0.254829592 + t * (-0.284496736 + t * (1.421413741 +
t * (-1.453152027 + t * 1.061405429))))
result = 1 - poly * math.exp(-x**2)
return result if x >= 0 else -result
def normal_cdf(x: float, mu: float = 0, sigma: float = 1) -> float:
"""
Função de distribuição acumulada normal.
P(X ≤ x)
"""
z = (x - mu) / (sigma * math.sqrt(2))
return 0.5 * (1 + error_function(z))
def z_test_proportions(p1: float, p2: float, n1: int, n2: int) -> dict:
"""
Teste z para diferença entre proporções.
Usado em testes A/B de taxa de conversão.
H₀: p₁ = p₂ (sem diferença)
H₁: p₁ ≠ p₂ (há diferença significativa)
"""
# Proporção combinada (pooled)
p_pool = (p1 * n1 + p2 * n2) / (n1 + n2)
# Erro padrão
se = math.sqrt(p_pool * (1 - p_pool) * (1/n1 + 1/n2))
if se == 0:
return {"error": "Erro padrão zero"}
# Estatística Z
z = (p1 - p2) / se
# P-valor (bilateral)
p_value = 2 * (1 - normal_cdf(abs(z)))
# Intervalo de confiança 95% para a diferença
diff = p1 - p2
margin = 1.96 * math.sqrt(p1*(1-p1)/n1 + p2*(1-p2)/n2)
ci_lower = diff - margin
ci_upper = diff + margin
return {
"z_score": z,
"p_value": p_value,
"significant": p_value < 0.05,
"confidence_interval": (ci_lower, ci_upper),
"effect_size": (p2 - p1) / p1 * 100 # Aumento percentual
}
# Teste A/B de landing page
# Variante A (controle): taxa de conversão original
# Variante B (teste): novo design da página
visitors_a = 5000
conversions_a = 250 # 5%
visitors_b = 5000
conversions_b = 300 # 6%
p_a = conversions_a / visitors_a
p_b = conversions_b / visitors_b
result = z_test_proportions(p_a, p_b, visitors_a, visitors_b)
print(f"=== RESULTADO DO TESTE A/B ===")
print(f"Variante A: {p_a:.1%} ({conversions_a}/{visitors_a})")
print(f"Variante B: {p_b:.1%} ({conversions_b}/{visitors_b})")
print(f"Z-Score: {result['z_score']:.4f}")
print(f"P-valor: {result['p_value']:.4f}")
print(f"Significativo? {'SIM ✓' if result['significant'] else 'NÃO ✗'}")
print(f"IC 95%: ({result['confidence_interval'][0]:.3f}, {result['confidence_interval'][1]:.3f})")
print(f"Aumento: {result['effect_size']:.1f}%")
#### Aplicação 2: Geração Procedural com Distribuição Normal
import random
import math
class ProceduralGenerator:
"""
Geração procedural usando distribuição normal.
Usada em jogos para criar mundos, NPCs e eventos naturais.
"""
def box_muller_transform(self) -> Tuple[float, float]:
"""
Transformada de Box-Muller: gera dois valores normais independentes
a partir de dois uniformes.
Z₁ = √(-2·ln(U₁)) · cos(2π·U₂)
Z₂ = √(-2·ln(U₁)) · sin(2π·U₂)
"""
u1 = random.random()
u2 = random.random()
z1 = math.sqrt(-2 * math.log(u1)) * math.cos(2 * math.pi * u2)
z2 = math.sqrt(-2 * math.log(u1)) * math.sin(2 * math.pi * u2)
return z1, z2
def generate_npc_stats(self, base_strength=50, std=10) -> dict:
"""Gera atributos de NPC com distribuição realista."""
z1, z2 = self.box_muller_transform()
z3, z4 = self.box_muller_transform()
return {
"strength": max(1, int(base_strength + z1 * std)),
"agility": max(1, int(45 + z2 * 12)),
"intelligence": max(1, int(40 + z3 * 15)),
"endurance": max(1, int(55 + z4 * 8)),
}
def generate_terrain_height(self, x, y, scale=0.1) -> float:
"""
Altura do terreno procedural usando somas de normais.
Base do Perlin Noise simplificado.
"""
random.seed(int(x * 1000 + y * 100))
z, _ = self.box_muller_transform()
return z * 50 # amplitude do terreno
gen = ProceduralGenerator()
print("=== NPCs GERADOS PROCEDURALMENTE ===")
for i in range(5):
stats = gen.generate_npc_stats()
total = sum(stats.values())
print(f"NPC {i+1}: {stats} | Total: {total}")
Parte VI — Criptografia e Segurança
Capítulo 10 — RSA: Baseado no Teorema de Euler
A criptografia RSA é baseada em duas equações fundamentais:
Cifragem: c = m^e mod n
Decifragem: m = c^d mod n
Onde e·d ≡ 1 (mod φ(n))
#### Implementação Educacional do RSA
import random
import math
def is_prime(n: int, k: int = 10) -> bool:
"""
Teste de primalidade Miller-Rabin.
Probabilístico: P(erro) < 4^(-k)
"""
if n < 2:
return False
if n == 2 or n == 3:
return True
if n % 2 == 0:
return False
# Escrever n-1 como 2^r · d
r, d = 0, n - 1
while d % 2 == 0:
r += 1
d //= 2
for _ in range(k):
a = random.randrange(2, n - 1)
x = pow(a, d, n) # a^d mod n
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n) # x² mod n
if x == n - 1:
break
else:
return False
return True
def generate_prime(bits: int = 512) -> int:
"""Gera um número primo de 'bits' bits."""
while True:
n = random.getrandbits(bits)
n |= (1 << bits - 1) | 1 # Garantir o tamanho e ser ímpar
if is_prime(n):
return n
def extended_gcd(a: int, b: int):
"""
Algoritmo de Euclides extendido.
Encontra x, y tal que: a·x + b·y = gcd(a, b)
"""
if a == 0:
return b, 0, 1
gcd, x1, y1 = extended_gcd(b % a, a)
return gcd, y1 - (b // a) * x1, x1
def modular_inverse(e: int, phi: int) -> int:
"""
Inverso modular de e mod phi.
Encontra d tal que: e·d ≡ 1 (mod phi)
"""
gcd, x, _ = extended_gcd(e % phi, phi)
if gcd != 1:
raise ValueError("Inverso modular não existe")
return (x % phi + phi) % phi
class RSA:
"""
Implementação educacional do RSA.
NÃO USE EM PRODUÇÃO — use cryptography ou openssl.
"""
def __init__(self, bits=512):
self._generate_keys(bits)
def _generate_keys(self, bits: int):
# 1. Escolher dois primos grandes p e q
p = generate_prime(bits // 2)
q = generate_prime(bits // 2)
# 2. n = p · q (módulo)
self.n = p * q
# 3. φ(n) = (p-1)(q-1) (totiente de Euler)
phi_n = (p - 1) * (q - 1)
# 4. Escolher e: 1 < e < φ(n) e gcd(e, φ(n)) = 1
self.e = 65537 # Escolha comum (primo de Fermat)
# 5. Calcular d: e·d ≡ 1 (mod φ(n))
self.d = modular_inverse(self.e, phi_n)
self.public_key = (self.e, self.n)
self.private_key = (self.d, self.n)
def encrypt(self, message: int) -> int:
"""c = m^e mod n"""
e, n = self.public_key
return pow(message, e, n)
def decrypt(self, ciphertext: int) -> int:
"""m = c^d mod n"""
d, n = self.private_key
return pow(ciphertext, d, n)
# Demonstração
rsa = RSA(bits=256) # Pequeno para demonstração
message = 42
print(f"Mensagem original: {message}")
encrypted = rsa.encrypt(message)
print(f"Mensagem cifrada: {encrypted}")
decrypted = rsa.decrypt(encrypted)
print(f"Mensagem decifrada: {decrypted}")
print(f"Correto: {message == decrypted}")
Capítulo 11 — Funções Hash e o Princípio da Avalanche
Uma boa função hash deve satisfazer:
H(x) ≠ H(y)para quase todox ≠ y(resistência a colisões)- Pequenas mudanças em
xcausam grandes mudanças emH(x)(efeito avalanche)
#### Aplicação: Tabela Hash com Endereçamento Aberto
class HashTable:
"""
Tabela hash com sondagem quadrática.
Hash function: h(k, i) = (h'(k) + c₁·i + c₂·i²) mod m
"""
def __init__(self, capacity=16):
self.capacity = capacity
self.size = 0
self.buckets = [None] * capacity
self.DELETED = object() # Marcador de deletado
def _hash(self, key) -> int:
"""
Função hash polinomial.
h(k) = Σ k[i] · 31^(n-1-i) mod m
Base 31 é primo — reduz colisões.
"""
if isinstance(key, int):
return key % self.capacity
hash_val = 0
for char in str(key):
hash_val = (hash_val * 31 + ord(char)) % self.capacity
return hash_val
def _probe(self, key, i: int) -> int:
"""
Sondagem quadrática: h(k,i) = (h'(k) + 0.5i + 0.5i²) mod m
Reduz clustering primário em comparação com sondagem linear.
"""
h_prime = self._hash(key)
return (h_prime + (i + i*i) // 2) % self.capacity
def put(self, key, value):
"""Inserção com sondagem quadrática."""
if self.size >= self.capacity * 0.7: # Fator de carga < 0.7
self._resize()
for i in range(self.capacity):
idx = self._probe(key, i)
if self.buckets[idx] is None or self.buckets[idx] is self.DELETED:
self.buckets[idx] = (key, value)
self.size += 1
return
if self.buckets[idx][0] == key:
self.buckets[idx] = (key, value) # Atualizar
return
raise Exception("Tabela cheia")
def get(self, key):
"""Busca com a mesma sequência de sondagem."""
for i in range(self.capacity):
idx = self._probe(key, i)
if self.buckets[idx] is None:
return None
if self.buckets[idx] is not self.DELETED and self.buckets[idx][0] == key:
return self.buckets[idx][1]
return None
def _resize(self):
"""Dobrar capacidade quando fator de carga excede 0.7."""
old_buckets = self.buckets
self.capacity *= 2
self.buckets = [None] * self.capacity
self.size = 0
for bucket in old_buckets:
if bucket and bucket is not self.DELETED:
self.put(bucket[0], bucket[1])
# Teste
ht = HashTable()
data = [("user:1", {"nome": "Alice", "email": "alice@ex.com"}),
("user:2", {"nome": "Bob", "email": "bob@ex.com"}),
("session:abc123", {"user_id": 1, "expires": 3600}),
("cache:products", [1, 2, 3, 4, 5])]
for key, value in data:
ht.put(key, value)
print("Resultado das buscas:")
for key, _ in data:
val = ht.get(key)
print(f" {key}: {val}")
Parte VII — Física no Desenvolvimento de Jogos
Capítulo 12 — Equações de Newton no Motor de Física
Segunda Lei de Newton: F = m·a
Integração de Euler: v(t+dt) = v(t) + a(t)·dt | x(t+dt) = x(t) + v(t)·dt
#### Implementação de Motor de Física 2D
import math
from dataclasses import dataclass, field
from typing import List
@dataclass
class Vec2:
x: float = 0.0
y: float = 0.0
def __add__(self, other): return Vec2(self.x + other.x, self.y + other.y)
def __sub__(self, other): return Vec2(self.x - other.x, self.y - other.y)
def __mul__(self, scalar): return Vec2(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar): return self.__mul__(scalar)
def magnitude(self): return math.sqrt(self.x**2 + self.y**2)
def normalized(self):
mag = self.magnitude()
return Vec2(self.x/mag, self.y/mag) if mag > 0 else Vec2(0, 0)
def dot(self, other): return self.x*other.x + self.y*other.y
def __repr__(self): return f"Vec2({self.x:.2f}, {self.y:.2f})"
@dataclass
class RigidBody:
position: Vec2 = field(default_factory=Vec2)
velocity: Vec2 = field(default_factory=Vec2)
acceleration: Vec2 = field(default_factory=Vec2)
mass: float = 1.0
restitution: float = 0.8 # Coeficiente de restituição (elasticidade)
friction: float = 0.1
force_accumulator: Vec2 = field(default_factory=Vec2)
def apply_force(self, force: Vec2):
"""Acumular forças para integração."""
self.force_accumulator = self.force_accumulator + force
def integrate(self, dt: float):
"""
Integração de Verlet (mais estável que Euler simples):
a(t) = F(t) / m [F = m·a]
x(t+dt) = x(t) + v(t)·dt [posição]
v(t+dt) = v(t) + a(t)·dt [velocidade]
Amortecimento: v *= (1 - damping·dt)
"""
if self.mass <= 0: # Corpo estático
return
# F = m·a → a = F/m
self.acceleration = self.force_accumulator * (1.0 / self.mass)
# Atualizar velocidade: v = v + a·dt
self.velocity = self.velocity + self.acceleration * dt
# Amortecimento linear (atrito do ar)
damping = 0.99
self.velocity = self.velocity * (damping ** dt)
# Atualizar posição: x = x + v·dt
self.position = self.position + self.velocity * dt
# Limpar acumulador de forças
self.force_accumulator = Vec2(0, 0)
class PhysicsWorld:
def __init__(self, gravity=Vec2(0, -9.81)):
self.bodies: List[RigidBody] = []
self.gravity = gravity
self.ground_y = 0.0
def add_body(self, body: RigidBody):
self.bodies.append(body)
def step(self, dt: float):
"""Passo de simulação."""
for body in self.bodies:
if body.mass > 0:
# Aplicar gravidade: F_grav = m·g
gravity_force = self.gravity * body.mass
body.apply_force(gravity_force)
body.integrate(dt)
# Colisão simples com o chão
if body.position.y < self.ground_y:
body.position.y = self.ground_y
# Reflexão com restituição: v_y' = -e·v_y
body.velocity.y = -body.velocity.y * body.restitution
# Atrito no chão
body.velocity.x *= (1 - body.friction)
# Simulação de projétil
world = PhysicsWorld(gravity=Vec2(0, -9.81))
projectile = RigidBody(
position=Vec2(0, 1),
velocity=Vec2(15, 20), # 15 m/s horizontal, 20 m/s vertical
mass=0.5,
restitution=0.6
)
world.add_body(projectile)
print("Tempo(s) | Posição X | Posição Y | Velocidade")
print("-" * 50)
dt = 0.05 # 50ms por passo
for step in range(60): # 3 segundos
t = step * dt
pos = projectile.position
vel = projectile.velocity
if step % 5 == 0:
print(f" {t:.2f}s | {pos.x:8.2f}m | {pos.y:8.2f}m | {vel.magnitude():.2f} m/s")
world.step(dt)
Capítulo 13 — Quaternions: Rotação 3D sem Gimbal Lock
Quaternions são a solução elegante para rotações 3D. Definição:
q = w + xi + yj + zk
i² = j² = k² = ijk = -1
#### Implementação de Quaternion para Motor 3D
import math
class Quaternion:
"""
Quaternion para representação de rotações 3D.
Evita gimbal lock que aflige os ângulos de Euler.
q = w + xi + yj + zk
"""
def __init__(self, w=1.0, x=0.0, y=0.0, z=0.0):
self.w = w
self.x = x
self.y = y
self.z = z
@classmethod
def from_axis_angle(cls, axis, angle_rad):
"""
Criar quaternion de rotação a partir de eixo e ângulo.
q = cos(θ/2) + sin(θ/2)·(xi + yj + zk)
"""
ax, ay, az = axis
# Normalizar eixo
mag = math.sqrt(ax**2 + ay**2 + az**2)
ax, ay, az = ax/mag, ay/mag, az/mag
half_angle = angle_rad / 2
sin_half = math.sin(half_angle)
return cls(
w=math.cos(half_angle),
x=ax * sin_half,
y=ay * sin_half,
z=az * sin_half
)
def __mul__(self, other):
"""
Multiplicação de quaternions (composição de rotações).
Não é comutativa: q₁ · q₂ ≠ q₂ · q₁
"""
w1, x1, y1, z1 = self.w, self.x, self.y, self.z
w2, x2, y2, z2 = other.w, other.x, other.y, other.z
return Quaternion(
w=w1*w2 - x1*x2 - y1*y2 - z1*z2,
x=w1*x2 + x1*w2 + y1*z2 - z1*y2,
y=w1*y2 - x1*z2 + y1*w2 + z1*x2,
z=w1*z2 + x1*y2 - y1*x2 + z1*w2
)
def conjugate(self):
"""q* = w - xi - yj - zk"""
return Quaternion(self.w, -self.x, -self.y, -self.z)
def normalize(self):
"""||q|| = √(w² + x² + y² + z²)"""
mag = math.sqrt(self.w**2 + self.x**2 + self.y**2 + self.z**2)
return Quaternion(self.w/mag, self.x/mag, self.y/mag, self.z/mag)
def rotate_vector(self, vx, vy, vz):
"""
Rotar vetor v usando: v' = q · v · q*
"""
v = Quaternion(0, vx, vy, vz)
q_conj = self.conjugate()
result = self * v * q_conj
return result.x, result.y, result.z
def slerp(self, other, t):
"""
Spherical Linear Interpolation — interpolação suave entre rotações.
q(t) = q₁ · (q₁⁻¹ · q₂)^t
Fundamental para animações 3D!
"""
cos_half = (self.w*other.w + self.x*other.x +
self.y*other.y + self.z*other.z)
# Garantir caminho mais curto
if cos_half < 0:
other = Quaternion(-other.w, -other.x, -other.y, -other.z)
cos_half = -cos_half
if cos_half > 0.9995:
# Interpolação linear para ângulos muito pequenos
return Quaternion(
self.w + t*(other.w - self.w),
self.x + t*(other.x - self.x),
self.y + t*(other.y - self.y),
self.z + t*(other.z - self.z)
).normalize()
half_angle = math.acos(cos_half)
sin_half = math.sqrt(1 - cos_half**2)
factor_a = math.sin((1-t) * half_angle) / sin_half
factor_b = math.sin(t * half_angle) / sin_half
return Quaternion(
factor_a*self.w + factor_b*other.w,
factor_a*self.x + factor_b*other.x,
factor_a*self.y + factor_b*other.y,
factor_a*self.z + factor_b*other.z
)
def to_euler_degrees(self):
"""Converter para ângulos de Euler (para debug)."""
# Roll (X)
sinr = 2*(self.w*self.x + self.y*self.z)
cosr = 1 - 2*(self.x**2 + self.y**2)
roll = math.degrees(math.atan2(sinr, cosr))
# Pitch (Y)
sinp = 2*(self.w*self.y - self.z*self.x)
sinp = max(-1, min(1, sinp))
pitch = math.degrees(math.asin(sinp))
# Yaw (Z)
siny = 2*(self.w*self.z + self.x*self.y)
cosy = 1 - 2*(self.y**2 + self.z**2)
yaw = math.degrees(math.atan2(siny, cosy))
return roll, pitch, yaw
# Exemplo: animação de câmera
q_start = Quaternion.from_axis_angle((0, 1, 0), math.radians(0))
q_end = Quaternion.from_axis_angle((0, 1, 0), math.radians(180))
print("Interpolação SLERP para animação de câmera:")
for t in [0, 0.25, 0.5, 0.75, 1.0]:
q = q_start.slerp(q_end, t)
roll, pitch, yaw = q.to_euler_degrees()
print(f" t={t}: Yaw = {yaw:.1f}°")
Parte VIII — Algoritmos Avançados e Equações Clássicas
Capítulo 14 — A Equação Logística: Caos e Sistemas Dinâmicos
x_(n+1) = r · x_n · (1 - x_n)
Esta simples equação gera comportamento caótico — a base de geradores de números pseudo-aleatórios e simulações populacionais.
#### Aplicação em Geração Procedural
class LogisticMapPRNG:
"""
Gerador de números pseudo-aleatórios baseado no mapa logístico.
x_(n+1) = r · x_n · (1 - x_n)
Para r ≈ 3.99, o sistema é caótico e imprevisível.
"""
def __init__(self, seed: float = 0.5, r: float = 3.9999):
assert 0 < seed < 1, "Seed deve estar em (0, 1)"
assert 3.57 < r <= 4.0, "r deve ser > 3.57 para caos"
self.x = seed
self.r = r
def next(self) -> float:
"""Gerar próximo número em [0, 1]."""
# Burn-in para entrar na região caótica
for _ in range(10):
self.x = self.r * self.x * (1 - self.x)
return self.x
def next_int(self, min_val: int, max_val: int) -> int:
return int(self.next() * (max_val - min_val + 1)) + min_val
def shuffle(self, lst: list) -> list:
"""Fisher-Yates shuffle com PRNG caótico."""
lst = lst.copy()
n = len(lst)
for i in range(n - 1, 0, -1):
j = self.next_int(0, i)
lst[i], lst[j] = lst[j], lst[i]
return lst
# Geração procedural de dungeon
class DungeonGenerator:
def __init__(self, seed=0.42):
self.prng = LogisticMapPRNG(seed)
def generate_room(self, x, y):
width = self.prng.next_int(5, 15)
height = self.prng.next_int(4, 12)
has_chest = self.prng.next() < 0.3
enemy_count = self.prng.next_int(0, 5)
return {
"position": (x, y),
"size": (width, height),
"has_chest": has_chest,
"enemies": enemy_count,
"loot_quality": round(self.prng.next(), 2)
}
gen = DungeonGenerator(seed=0.7654)
print("Salas geradas proceduralmente:")
for i in range(5):
room = gen.generate_room(i*20, 0)
print(f" Sala {i+1}: {room['size'][0]}x{room['size'][1]}, "
f"Inimigos: {room['enemies']}, "
f"Tesouro: {'SIM' if room['has_chest'] else 'NÃO'}, "
f"Qualidade: {room['loot_quality']}")
Capítulo 15 — Lei de Amdahl: Limites do Paralelismo
S(n) = 1 / ((1 - p) + p/n)
Onde p = fração paralelizável e n = número de processadores.
#### Aplicação em Arquitetura de Software
def amdahls_law(parallelizable_fraction: float, num_processors: int) -> float:
"""
Lei de Amdahl: speedup máximo teórico com n processadores.
S(n) = 1 / ((1-p) + p/n)
Limite quando n → ∞: S_max = 1/(1-p)
"""
serial_fraction = 1 - parallelizable_fraction
return 1 / (serial_fraction + parallelizable_fraction / num_processors)
def gustafson_barsis(serial_fraction: float, num_processors: int) -> float:
"""
Lei de Gustafson-Barsis: visão alternativa mais otimista.
S(n) = n - serial_fraction · (n - 1)
Considera que problemas maiores têm mais paralelismo.
"""
return num_processors - serial_fraction * (num_processors - 1)
# Análise de um sistema de processamento de dados
print("=== ANÁLISE DE ESCALABILIDADE (Lei de Amdahl) ===")
print()
# 90% do trabalho é paralelizável
p = 0.90
print(f"Fração paralelizável: {p:.0%}")
print(f"Speedup máximo teórico (n→∞): {1/(1-p):.0f}x")
print()
print(f"{'Processadores':>15} | {'Speedup (Amdahl)':>16} | {'Eficiência':>10}")
print("-" * 48)
for n in [1, 2, 4, 8, 16, 32, 64, 128, 256]:
speedup = amdahls_law(p, n)
efficiency = speedup / n * 100
print(f"{n:>15} | {speedup:>16.2f}x | {efficiency:>9.1f}%")
print()
print("=== IMPLICAÇÕES PARA ARQUITETURA ===")
print()
# Comparar sistemas com diferentes frações paralelizáveis
systems = [
("Processamento de Imagens", 0.99),
("Servidor Web (I/O bound)", 0.95),
("Compilador", 0.80),
("Sistema com DB bottleneck", 0.50),
]
print(f"{'Sistema':>30} | {'Máx speedup':>11} | {'Speedup c/16 CPU':>16}")
print("-" * 65)
for name, p_val in systems:
max_speedup = 1/(1-p_val)
speedup_16 = amdahls_law(p_val, 16)
print(f"{name:>30} | {max_speedup:>10.1f}x | {speedup_16:>15.1f}x")
Parte IX — Teoria da Informação
Capítulo 16 — Compressão com Huffman: L = Σ p(x)·l(x)
A codificação de Huffman minimiza o comprimento médio dos símbolos usando árvores binárias baseadas na teoria da informação de Shannon.
L_médio = Σ p(xᵢ) · l(xᵢ)
#### Implementação Completa
import heapq
from collections import Counter
from typing import Dict, Optional
class HuffmanNode:
def __init__(self, char: Optional[str], freq: int,
left=None, right=None):
self.char = char
self.freq = freq
self.left = left
self.right = right
def __lt__(self, other):
return self.freq < other.freq
class HuffmanCoding:
"""
Codificação de Huffman: compressão sem perdas ótima
para símbolos independentes.
Baseada no teorema de Shannon:
Comprimento médio ≥ Entropia H(X)
"""
def __init__(self):
self.codes: Dict[str, str] = {}
self.root = None
def build_tree(self, text: str) -> HuffmanNode:
"""Construir árvore de Huffman a partir das frequências."""
freq_map = Counter(text)
# Fila de prioridade min-heap
heap = [HuffmanNode(char, freq) for char, freq in freq_map.items()]
heapq.heapify(heap)
while len(heap) > 1:
# Pegar os dois nós de menor frequência
left = heapq.heappop(heap)
right = heapq.heappop(heap)
# Criar nó pai
parent = HuffmanNode(
char=None,
freq=left.freq + right.freq,
left=left,
right=right
)
heapq.heappush(heap, parent)
self.root = heap[0]
return self.root
def _generate_codes(self, node, current_code=""):
"""Percorrer árvore e gerar códigos binários."""
if node is None:
return
if node.char is not None:
self.codes[node.char] = current_code or "0"
return
self._generate_codes(node.left, current_code + "0")
self._generate_codes(node.right, current_code + "1")
def encode(self, text: str) -> str:
"""Comprimir texto."""
self.build_tree(text)
self._generate_codes(self.root)
encoded = "".join(self.codes[char] for char in text)
return encoded
def decode(self, encoded: str) -> str:
"""Descomprimir bitstring."""
result = []
current = self.root
for bit in encoded:
current = current.left if bit == "0" else current.right
if current.char is not None:
result.append(current.char)
current = self.root
return "".join(result)
def compression_stats(self, text: str, encoded: str) -> dict:
"""Calcular estatísticas de compressão."""
original_bits = len(text) * 8 # ASCII: 8 bits por char
compressed_bits = len(encoded)
avg_code_length = sum(
len(code) * (text.count(char) / len(text))
for char, code in self.codes.items()
)
import math
freq_map = Counter(text)
entropy = -sum(
(freq/len(text)) * math.log2(freq/len(text))
for freq in freq_map.values()
)
return {
"original_bits": original_bits,
"compressed_bits": compressed_bits,
"compression_ratio": original_bits / compressed_bits,
"space_saving": (1 - compressed_bits/original_bits) * 100,
"avg_code_length": avg_code_length,
"entropy": entropy,
"efficiency": entropy / avg_code_length * 100
}
# Comprimir um texto real
text = """
A codificação de Huffman é um algoritmo de compressão sem perdas
que atribui códigos binários de comprimento variável para cada símbolo,
com os símbolos mais frequentes recebendo os códigos mais curtos.
Este método é amplamente usado em ZIP, GZIP, PNG e MP3.
""".strip()
coder = HuffmanCoding()
encoded = coder.encode(text)
decoded = coder.decode(encoded)
stats = coder.compression_stats(text, encoded)
print(f"=== HUFFMAN CODING ===")
print(f"Texto original: {len(text)} caracteres")
print(f"Bits originais: {stats['original_bits']} bits")
print(f"Bits comprimidos: {stats['compressed_bits']} bits")
print(f"Taxa de compressão: {stats['compression_ratio']:.2f}x")
print(f"Economia de espaço: {stats['space_saving']:.1f}%")
print(f"Comprimento médio do código: {stats['avg_code_length']:.3f} bits/char")
print(f"Entropia teórica: {stats['entropy']:.3f} bits/char")
print(f"Eficiência: {stats['efficiency']:.1f}%")
print(f"Decodificação correta: {text == decoded}")
Parte X — Geometria Computacional e Gráficos
Capítulo 17 — Equação de Bézier para Animações Fluidas
As curvas de Bézier são o coração do design vetorial, animações CSS e fontes tipográficas.
Cúbica: B(t) = (1-t)³P₀ + 3(1-t)²tP₁ + 3(1-t)t²P₂ + t³P₃
from typing import Tuple
import math
def bezier_cubic(t: float,
p0: Tuple[float, float],
p1: Tuple[float, float],
p2: Tuple[float, float],
p3: Tuple[float, float]) -> Tuple[float, float]:
"""
Curva de Bézier cúbica.
B(t) = (1-t)³P₀ + 3(1-t)²tP₁ + 3(1-t)t²P₂ + t³P₃
Polinômios de Bernstein de grau 3.
"""
u = 1 - t
x = (u**3 * p0[0] +
3 * u**2 * t * p1[0] +
3 * u * t**2 * p2[0] +
t**3 * p3[0])
y = (u**3 * p0[1] +
3 * u**2 * t * p1[1] +
3 * u * t**2 * p2[1] +
t**3 * p3[1])
return (x, y)
def css_easing(name: str) -> Tuple:
"""
Curvas de easing do CSS como curvas de Bézier.
Estas são as equações por baixo de 'ease-in', 'ease-out', etc.
"""
easings = {
"ease": ((0.25, 0.1), (0.25, 1.0)), # P1, P2
"ease-in": ((0.42, 0.0), (1.0, 1.0)),
"ease-out": ((0.0, 0.0), (0.58, 1.0)),
"ease-in-out": ((0.42, 0.0), (0.58, 1.0)),
"linear": ((0.0, 0.0), (1.0, 1.0)),
}
return easings.get(name, easings["ease"])
class AnimationEasing:
"""
Sistema de easing para animações de UI.
Converte tempo linear em progresso com aceleração/desaceleração.
"""
def __init__(self, easing_name="ease-in-out"):
p1, p2 = css_easing(easing_name)
# Pontos fixos: P0=(0,0), P3=(1,1)
self.p0 = (0.0, 0.0)
self.p1 = p1
self.p2 = p2
self.p3 = (1.0, 1.0)
def evaluate(self, t: float) -> float:
"""
Dado tempo normalizado t ∈ [0,1],
retorna progresso com easing.
"""
x, y = bezier_cubic(t, self.p0, self.p1, self.p2, self.p3)
return y
def animate_property(self, start, end, duration_ms, current_time_ms):
"""Interpolar propriedade com easing."""
t = max(0, min(1, current_time_ms / duration_ms))
eased_t = self.evaluate(t)
return start + (end - start) * eased_t
# Simular animação de um modal aparecendo na tela
easing = AnimationEasing("ease-out")
print("Animação de modal (opacity 0→1, 300ms):")
print(f"{'Tempo':>8} | {'Linear':>8} | {'Ease-out':>10}")
print("-" * 32)
for ms in range(0, 350, 50):
t_linear = min(1.0, ms / 300)
t_eased = easing.evaluate(t_linear)
opacity = t_eased * 100
print(f"{ms:>5}ms | {t_linear*100:>6.1f}% | {opacity:>8.1f}%")
Parte XI — Equações na Computação em Nuvem e Sistemas Distribuídos
Capítulo 18 — Teorema CAP e a Lei de Little
Lei de Little: L = λ · W
Onde L = itens no sistema, λ = taxa de chegada, W = tempo médio no sistema.
class QueueingSystem:
"""
Análise de sistemas de filas usando a Lei de Little e
modelo M/M/c (Erlang).
Fundamental para dimensionar servidores, threads e workers.
"""
def __init__(self, arrival_rate: float, service_rate: float, num_servers: int = 1):
self.lambda_ = arrival_rate # λ: chegadas por segundo
self.mu = service_rate # μ: atendimentos por segundo por servidor
self.c = num_servers # c: número de servidores
self.rho = arrival_rate / (num_servers * service_rate) # Utilização
def littles_law_check(self, L: float, W: float) -> float:
"""
Lei de Little: L = λ · W
L = número médio de itens no sistema
λ = taxa de chegada
W = tempo médio no sistema
"""
return self.lambda_ * W # Deve ≈ L
def erlang_c(self) -> float:
"""
Fórmula de Erlang-C: probabilidade de espera em fila M/M/c.
P(espera) = [A^c/c! · c/(c·μ - λ)] / [Σ A^n/n! + A^c/c! · c/(c·μ - λ)]
Onde A = λ/μ (tráfego oferecido).
"""
import math
if self.rho >= 1:
return 1.0 # Sistema instável!
A = self.lambda_ / self.mu # Tráfego em Erlangs
# Numerador
numerator = (A**self.c / math.factorial(self.c)) * (1 / (1 - self.rho))
# Denominador (soma da série)
denominator = sum(A**n / math.factorial(n) for n in range(self.c))
denominator += numerator
return numerator / denominator
def average_queue_length(self) -> float:
"""Lq = P(espera) · ρ / (1-ρ)"""
Cw = self.erlang_c()
return Cw * self.rho / (1 - self.rho)
def average_wait_time(self) -> float:
"""Wq = Lq / λ (Lei de Little)"""
return self.average_queue_length() / self.lambda_
def average_response_time(self) -> float:
"""W = Wq + 1/μ (tempo na fila + tempo de serviço)"""
return self.average_wait_time() + 1/self.mu
def analyze(self):
print(f"=== ANÁLISE DO SISTEMA DE FILAS ===")
print(f"Taxa de chegada (λ): {self.lambda_:.1f} req/s")
print(f"Taxa de serviço (μ): {self.mu:.1f} req/s/servidor")
print(f"Servidores (c): {self.c}")
print(f"Utilização (ρ): {self.rho:.1%}")
print()
if self.rho >= 1:
print("⚠️ SISTEMA INSTÁVEL! ρ ≥ 1")
print(" A fila crescerá indefinidamente.")
return
Cw = self.erlang_c()
Lq = self.average_queue_length()
Wq = self.average_wait_time()
W = self.average_response_time()
L = self.lambda_ * W # Lei de Little
print(f"P(esperar na fila): {Cw:.1%}")
print(f"Tamanho médio da fila (Lq): {Lq:.2f} requests")
print(f"Tempo médio de espera (Wq): {Wq*1000:.1f}ms")
print(f"Tempo médio de resposta (W): {W*1000:.1f}ms")
print(f"Requests no sistema - Lei de Little (L=λW): {L:.2f}")
# Dimensionar um serviço de API REST
# 100 req/s chegando, cada request leva 20ms para processar
system = QueueingSystem(
arrival_rate=100, # 100 requisições por segundo
service_rate=1/0.020, # 50 req/s por servidor (20ms por req)
num_servers=3
)
system.analyze()
Parte XII — Inteligência Artificial e Machine Learning
Capítulo 19 — A Equação de Bellman: Aprendizado por Reforço
V(s) = max_a [R(s,a) + γ · Σ P(s'|s,a) · V(s')]
#### Implementação de Q-Learning
import random
import numpy as np
from collections import defaultdict
class QLearningAgent:
"""
Agente Q-Learning baseado na Equação de Bellman.
Q(s,a) ← Q(s,a) + α[r + γ·max_a' Q(s',a') - Q(s,a)]
Onde:
α = taxa de aprendizado
γ = fator de desconto (horizonte temporal)
ε = exploração vs exploração
"""
def __init__(self, n_actions: int, alpha=0.1, gamma=0.99, epsilon=0.1):
self.n_actions = n_actions
self.alpha = alpha # Taxa de aprendizado
self.gamma = gamma # Fator de desconto
self.epsilon = epsilon # Política ε-greedy
# Tabela Q inicializada com zeros
self.q_table = defaultdict(lambda: np.zeros(n_actions))
def choose_action(self, state) -> int:
"""
Política ε-greedy:
- Com prob ε: explorar (ação aleatória)
- Com prob 1-ε: exploitar (melhor ação conhecida)
"""
if random.random() < self.epsilon:
return random.randint(0, self.n_actions - 1)
return np.argmax(self.q_table[state])
def learn(self, state, action: int, reward: float, next_state, done: bool):
"""
Atualização Q-Learning (Bellman):
Q(s,a) ← Q(s,a) + α[r + γ·max_a' Q(s',a') - Q(s,a)]
"""
current_q = self.q_table[state][action]
if done:
target = reward
else:
# Bellman: valor do próximo estado
max_next_q = np.max(self.q_table[next_state])
target = reward + self.gamma * max_next_q
# Atualização incremental
td_error = target - current_q # Temporal Difference Error
self.q_table[state][action] += self.alpha * td_error
return abs(td_error)
class GridWorld:
"""
Ambiente Grid World simples para demonstrar Q-Learning.
G = Goal (+10)
T = Trap (-5)
. = Empty (-.01)
4x4 grid:
. . . .
. T . .
. . . G
. . . .
"""
def __init__(self):
self.size = 4
self.goal = (2, 3)
self.trap = (1, 1)
self.start = (0, 0)
self.reset()
def reset(self):
self.pos = list(self.start)
return tuple(self.pos)
def step(self, action):
"""
Ações: 0=cima, 1=baixo, 2=esquerda, 3=direita
"""
moves = [(-1,0), (1,0), (0,-1), (0,1)]
dy, dx = moves[action]
new_y = max(0, min(self.size-1, self.pos[0] + dy))
new_x = max(0, min(self.size-1, self.pos[1] + dx))
self.pos = [new_y, new_x]
state = tuple(self.pos)
if state == self.goal:
return state, 10.0, True
elif state == self.trap:
return state, -5.0, False
else:
return state, -0.01, False
# Treinar agente
env = GridWorld()
agent = QLearningAgent(n_actions=4, alpha=0.1, gamma=0.95, epsilon=0.2)
n_episodes = 2000
rewards_per_episode = []
for episode in range(n_episodes):
state = env.reset()
total_reward = 0
for _ in range(100): # Max 100 steps por episódio
action = agent.choose_action(state)
next_state, reward, done = env.step(action)
agent.learn(state, action, reward, next_state, done)
state = next_state
total_reward += reward
if done:
break
rewards_per_episode.append(total_reward)
# Avaliar política aprendida
agent.epsilon = 0 # Modo puro de exploração
successes = 0
for _ in range(100):
state = env.reset()
for _ in range(50):
action = agent.choose_action(state)
state, _, done = env.step(action)
if done:
successes += 1
break
print(f"Taxa de sucesso após {n_episodes} episódios: {successes}%")
print(f"Recompensa média (últimos 100 eps): {sum(rewards_per_episode[-100:])/100:.2f}")
Conclusão — A Linguagem Universal do Software
Percorremos um caminho extraordinário neste artigo — da identidade de Euler às redes neurais, de Pitágoras ao roteamento GPS, dos quaternions às animações CSS, de Bayes ao spam, de Shannon à compressão ZIP.
O que todos esses exemplos têm em comum? São todos software em produção, rodando agora mesmo em sistemas que você usa todos os dias. A música que você ouve no Spotify passa pela FFT de Euler. O GPS do seu carro usa Dijkstra. A recomendação da Netflix usa SVD. O ChatGPT usa gradiente descendente. O HTTPS que protege seu banco usa RSA. O Fortnite usa quaternions e física newtoniana.
O Mapa das Equações
| Equação | Campo | Aplicação Principal |
|---|---|---|
| e^(iπ) + 1 = 0 | Análise Complexa | FFT, processamento de sinais |
| a² + b² = c² | Geometria | Colisão, GPS, pathfinding |
| log₂(n) | Teoria da Info | Complexidade, compressão |
| A = UΣVᵀ | Álgebra Linear | Recomendação, compressão |
| θ -= α∇J | Cálculo | Treinamento de redes neurais |
| P(A|B) = P(B|A)P(A)/P(B) | Probabilidade | Spam, diagnóstico, ML |
| d[v] = min(d[u]+w) | Grafos | GPS, redes, jogos |
| F = ma | Física | Motores de física, jogos |
| q = w+xi+yj+zk | Álgebra | Rotação 3D, animações |
| c = m^e mod n | Teoria dos Números | RSA, HTTPS, blockchain |
| H = -Σp·log₂p | Info Theory | Compressão, ML, AI |
| L = λ·W | Teoria das Filas | Cloud, servidores |
| Q(s,a) += α[r+γV - Q] | Otimização | Reinforcement Learning |
A Mensagem Final
Matemática não é o inimigo do desenvolvedor — é sua superpotência secreta. Cada vez que você escreve Math.sqrt(), np.dot(), scipy.optimize() ou usa uma biblioteca de machine learning, está sobre os ombros de gigantes: Euler, Gauss, Newton, Shannon, Turing.
Entender as equações por baixo do código não te torna apenas um programador melhor — te torna capaz de inventar soluções que outros nem sequer conseguem imaginar.
As ferramentas mudam. Os frameworks vêm e vão. As linguagens evoluem. Mas e^(iθ) = cos(θ) + i·sin(θ) será verdade para sempre.
A matemática é o único código que nunca fica obsoleto.
Escrito para desenvolvedores que querem entender o mundo por dentro.
Referências e Leitura Adicional
- "The Art of Computer Programming" — Donald Knuth
- "Numerical Recipes" — Press, Teukolsky, Vetterling, Flannery
- "Pattern Recognition and Machine Learning" — Christopher Bishop
- "Introduction to Algorithms" — Cormen, Leiserson, Rivest, Stein (CLRS)
- "Mathematics for 3D Game Programming" — Eric Lengyel
- "Elements of Information Theory" — Cover & Thomas
- "Reinforcement Learning: An Introduction" — Sutton & Barto
- 3Blue1Brown (YouTube) — Visualizações matemáticas extraordinárias
- Brilliant.org — Matemática interativa para desenvolvedores
© 2026 — Conteúdo licenciado para uso educacional