Erro e Condicionamento
intermediario computacao-cientifica, otimizacao analise-numericaErro e Condicionamento
Há duas perguntas independentes diante de um resultado numérico ruim: o problema é sensível? e o algoritmo é bom? Confundir as duas leva a culpar o código quando a culpa é do problema, ou vice-versa.
Tipos de erro
$$\text{erro absoluto} = |\hat x - x|, \qquad \text{erro relativo} = \frac{|\hat x - x|}{|x|}$$
O erro relativo é quase sempre o que interessa, porque é adimensional e comparável entre escalas. Um erro absoluto de $1$ metro é irrelevante ao medir a distância à Lua e catastrófico ao usinar uma peça.
Fontes de erro:
| Fonte | Origem |
|---|---|
| Arredondamento | Precisão finita da máquina |
| Truncamento | Substituir um processo infinito por finito |
| Dados | Medição imprecisa da entrada |
| Modelo | O modelo não descreve a realidade |
Os dois primeiros são do domínio da análise numérica; os dois últimos, da modelagem — e frequentemente dominam.
Condicionamento
O número de condição mede quanto o problema amplifica erros da entrada, independentemente do algoritmo:
$$\kappa = \frac{\text{erro relativo na saída}}{\text{erro relativo na entrada}}$$
Para uma função diferenciável:
$$\kappa(x) = \left|\frac{x f'(x)}{f(x)}\right|$$
Para sistemas lineares:
$$\kappa(A) = |A|,|A^{-1}| = \frac{\sigma_1}{\sigma_n}$$
Regra de bolso: com $\kappa \approx 10^k$, perdem-se cerca de $k$ dígitos. Em float64 (16 dígitos), $\kappa = 10^{12}$ deixa quatro dígitos confiáveis; $\kappa = 10^{16}$ não deixa nenhum.
Um problema mal condicionado é intrinsecamente difícil: nenhum algoritmo o resolve bem em precisão finita. A solução não é procurar código melhor — é reformular o problema, regularizar ou aumentar a precisão.
Estabilidade
Estabilidade é propriedade do algoritmo, não do problema. Um algoritmo é estável no sentido regressivo quando o resultado calculado é a solução exata de um problema ligeiramente perturbado:
$$\hat y = f(\tilde x), \qquad \tilde x \approx x$$
Essa é a formulação mais útil, porque separa claramente as responsabilidades. A relação final é:
$$\text{erro direto} \lesssim \kappa \times \text{erro regressivo}$$
Ou seja: algoritmo estável + problema bem condicionado = resultado confiável. Se qualquer um dos dois falhar, o resultado não é.
Exemplos que ilustram a distinção
Problema mal condicionado. Calcular as raízes do polinômio de Wilkinson $\prod_{k=1}^{20}(x-k)$ a partir dos coeficientes. Uma perturbação de $2^{-23}$ num único coeficiente move as raízes em várias unidades. Não há algoritmo que salve — a informação simplesmente não está nos coeficientes.
Algoritmo instável para problema bem condicionado. Calcular a variância por $E[X^2]-(E[X])^2$ com dados de média grande. O problema é bem condicionado; a fórmula é que cancela. O algoritmo de Welford resolve o mesmo problema de forma estável.
Eliminação sem pivoteamento. Resolver $Ax=b$ com $A$ bem condicionada mas pivô pequeno. O problema está bom; o algoritmo é que amplifica o erro. Com pivoteamento parcial, o mesmo problema é resolvido corretamente.
Diagnóstico prático
import numpy as np
k = np.linalg.cond(A)
if k > 1e12:
print(f'atenção: kappa = {k:.1e}, resultado pouco confiável')
# resíduo pequeno NÃO garante solução precisa
x = np.linalg.solve(A, b)
residuo = np.linalg.norm(A @ x - b) / np.linalg.norm(b)
O último ponto merece ênfase: em problema mal condicionado, um resíduo minúsculo é perfeitamente compatível com uma solução muito errada. Resíduo pequeno indica estabilidade regressiva, não precisão. Verifique o condicionamento antes de confiar.
O que fazer com problema mal condicionado
Reformular. Muitas vezes existe formulação equivalente e melhor condicionada. Usar QR em vez de equações normais reduz $\kappa^2$ a $\kappa$.
Regularizar. Acrescentar $\lambda I$ (Tikhonov / Ridge) limita o condicionamento ao custo de introduzir viés. Truncar valores singulares pequenos na pseudoinversa tem o mesmo efeito.
Escalar. Colunas com unidades de magnitudes muito diferentes inflam artificialmente o condicionamento. Normalizar atributos costuma resolver — é o mesmo motivo pelo qual padronizar entradas acelera o treino de redes neurais.
Aumentar a precisão. float128 ou aritmética arbitrária compram dígitos, ao custo de desempenho.
Exemplo trabalhado
$$A = \begin{pmatrix} 1 & 1 \ 1 & 1{,}0001 \end{pmatrix}$$
As linhas são quase paralelas, então $A$ é quase singular. Seu número de condição é da ordem de $4\times10^4$, o que significa perder cerca de 4 ou 5 dígitos.
Geometricamente: as duas retas do sistema se cruzam num ângulo minúsculo, e mover qualquer uma delas ligeiramente desloca muito o ponto de interseção. Nenhum algoritmo contorna isso.
Erros comuns
- Culpar o algoritmo por um problema mal condicionado.
- Confiar em resíduo pequeno como prova de precisão.
- Não escalar os dados e inflar o condicionamento sem necessidade.
- Comparar erros absolutos entre grandezas de escalas diferentes.
- Aumentar a precisão como primeira resposta, em vez de reformular.
Leituras recomendadas
- Trefethen e Bau, lições 12 a 15 — condicionamento e estabilidade explicados com clareza excepcional.
- Higham, Accuracy and Stability of Numerical Algorithms — a referência definitiva.
- Documentação do
numpy.linalg.conde doscipy.linalg— diagnósticos disponíveis nas ferramentas.
Lloyd N. Trefethen and David Bau III (1997). Numerical Linear Algebra. SIAM. DOI: 10.1137/1.9780898719574. Nicholas J. Higham (2002). Accuracy and Stability of Numerical Algorithms. SIAM. DOI: 10.1137/1.9780898718027.
Referências
- Lloyd N. Trefethen; David Bau III (1997). Numerical Linear Algebra. SIAM.
- Nicholas J. Higham (2002). Accuracy and Stability of Numerical Algorithms. SIAM.