Condicionamento e Estabilidade Numérica
Objetivos de Aprendizagem
- Definir o número de condição relativo de um problema, calculá-lo com suas hipóteses e dizer onde ele explode.
- Distinguir condicionamento, uma propriedade do problema, de estabilidade, uma propriedade do algoritmo.
- Calcular o erro direto e o erro reverso de uma resposta calculada.
- Usar a regra de que o erro direto é no máximo cerca do número de condição vezes o erro reverso, e dizer o que ela promete e o que não promete.
- Diagnosticar uma fórmula instável e trocá-la por uma estável, ou reconhecer que nenhuma troca pode ajudar.
Contexto e Motivação
O Exemplo 2 de sources-of-error-truncation-rounding-propagation perdeu todos os dígitos de , e uma reescrita recuperou todos. Essa reescrita nem sempre vai existir. Duas falhas parecem idênticas na tela e pedem respostas opostas. Às vezes a resposta é tão sensível à entrada que arredondar a entrada já a destrói, e só dados melhores ou outra pergunta podem ajudar. Às vezes o problema é perfeitamente saudável e quem desperdiça os dígitos é a fórmula.
Separar esses casos é o assunto desta lição, e isso decide o próximo passo: reescrever o código, aumentar a precisão ou questionar a entrada. O mesmo vocabulário atravessa a disciplina. bisection-and-convergence-rate lê um resíduo pequeno como erro reverso, e the-matrix-condition-number estende o número de condição para .
Teoria Central
Condicionamento: quão sensível é o problema
Trate um problema como uma função dos dados para a resposta. O condicionamento pergunta quanto a resposta exata se move quando os dados se movem, sem nenhum algoritmo envolvido. Se , e é derivável em , o número de condição relativo é
o limite, quando a perturbação encolhe, da variação relativa de sobre a variação relativa de . Assim, uma perturbação relativa na entrada move a resposta em cerca de . Entradas guardadas têm erro relativo de até , então mesmo um algoritmo perfeito deve esperar um erro relativo perto de em float64, uma perda de cerca de dígitos. A afirmação é de primeira ordem. Um problema com perto de é bem condicionado, e um com grande é mal condicionado.
| Problema | Grande quando | Fórmula no ponto de amostra | Medido | |
|---|---|---|---|---|
| nunca | em | |||
| grande | em | |||
| perto de | em | |||
| perto de , ; a maioria dos grandes | em | |||
| perto de | em |
A coluna medida perturba por um relativo de em aritmética de alta precisão e divide as duas variações relativas. Para , o tamanho sozinho também pesa: em , a menos que esteja perto de , onde se anula. A última linha é a subtração com exato, então só carrega erro. O fator da lição anterior supõe erro nos dois operandos, e aqui daria .
As hipóteses importam. Num zero de o número relativo não existe, e a versão absoluta assume. math.sin(math.pi) devolve , e isso não é defeito de sin: o math.pi guardado difere de exatamente por esse valor, e tem inclinação ali.
O condicionamento também depende do que conta como dado. Como função de , é mal condicionado em , porque arredondar já danificou . Como função de , tem perto de . É por isso que math.log1p(delta) existe: quando é conhecido exatamente, passe .
Estabilidade: quão bom é o algoritmo
Um algoritmo calcula . Seu erro direto é a distância até a resposta verdadeira, , em geral tomada em relação a . Seu erro reverso faz outra pergunta: que problema o algoritmo resolveu exatamente? É a menor perturbação relativa com .
Um algoritmo é reversamente estável (backward stable) se seu erro reverso é um múltiplo modesto de para toda entrada. De modo mais solto, um algoritmo é estável se seu erro direto não é muito maior que , o que o condicionamento já impõe. Um algoritmo instável perde mais do que o problema exige.
Ligando os dois
Se , a definição de dá, em primeira ordem,
Isso é uma cota superior, e se lê como diagnóstico:
- Erro reverso perto de e moderado: a resposta é precisa.
- Erro reverso perto de e enorme: a resposta pode ser ruim, e o algoritmo não tem culpa.
- Erro direto muito acima de : o algoritmo tem culpa.
Como fórmulas ficam instáveis
Um algoritmo é uma cadeia de passos, e cada passo é um pequeno problema com seu próprio número de condição. Uma rota comum de falha é um todo bem condicionado que passa por um passo mal condicionado: os erros dos passos anteriores são ampliados, e nada adiante os recupera. Os dígitos perdidos de seguem esse padrão. A função inteira tem , que tende a quando , então o problema é bem condicionado. O passo ruim é o último: em tem , que vale em . A reescrita não tem esse passo. Os dois exemplos instáveis abaixo falham assim.
Exemplos Resolvidos
Exemplo 1: um problema saudável, uma fórmula ruim
Resolva com e . As raízes são e . Derivar dá os números de condição de uma raiz em relação a cada coeficiente: e . Para os dois valem com muitos dígitos. O problema é bem condicionado.
A fórmula de livro subtrai de . Esse único passo tem número de condição em torno de . A alternativa estável calcula com os sinais que se somam e depois usa :
textfórmula r2 erro direto erro reverso em c (-b - sqrt(D))/2 7.450580596923828e-09 2.5e-01 2.5e-01 c / r1 1e-08 7.9e-17 7.9e-17
O erro reverso vem de , o coeficiente para o qual é raiz exata. A fórmula ruim resolveu uma equação com errado em 25 por cento e, com , o erro direto coincide, como a cota prevê. numpy.roots([1, -1e8, 1]) também vai bem aqui, com erro relativo de nessa raiz.
A reescrita supõe e ajuda com o cancelamento entre e a raiz quadrada. Se , as raízes quase coincidem, o próprio problema é mal condicionado e nenhuma reescrita devolve os dígitos perdidos.
Exemplo 2: os mesmos números, calculados em duas direções
Seja . Como , vale para , com . De em sai também . A recorrência para a frente, em float64:
textn I_n para a frente I_n exato erro razão 0 0.09531017980432493 0.09531017980432487 7.48e-17 1 0.04689820195675065 0.0468982019567514 7.48e-16 10.00 5 0.015352900839845474 0.015352900847328939 7.48e-12 10.00 10 0.008328713865253717 0.008327965518886312 7.48e-07 10.00 14 0.013578878977397152 0.006095415303348168 7.48e-03 10.00 16 0.7537212310730486 0.005374863668150086 7.48e-01 10.00 18 74.83944336874276 0.004806628252917123 7.48e+01 10.00
Cada passo multiplica o erro por , como a recorrência diz, então um erro de arredondamento comum de em chega a em . Em o valor já está acima da cota superior .
Rode para trás, , e cada passo divide o erro por . Partindo do chute grosseiro (valor verdadeiro ), o erro é em e em , onde o resultado difere de scipy.integrate.quad só no último dígito. Os mesmos números, por dois caminhos: um instável, outro estável. O problema nunca foram os números. A rota para a frente passa pelo subproblema , que amplia o erro absoluto por .
Exemplo 3: um bom algoritmo, um problema terrível
Tome o polinômio de Wilkinson de grau 20, , com coeficientes . Perturbar cada por um relativo move a raiz em cerca de , então seu número de condição em relação aos coeficientes é
numpy.roots, que calcula autovalores da matriz companheira, dá nos coeficientes expandidos:
textk raiz calculada erro direto relativo kappa_k 1 0.9999999999999833 1.7e-14 4.2e+02 5 5.000000877361879 1.8e-07 8.2e+08 10 9.990574635182616 9.4e-04 5.6e+12 14 13.935190089077999 4.6e-03 5.4e+13 20 19.999888806572073 5.6e-06 1.4e+11
Agora expanda em aritmética exata. Seus coeficientes diferem dos verdadeiros por um relativo de no máximo . Logo, as raízes calculadas são as raízes exatas de um polinômio que é quase o certo: o erro reverso é minúsculo, enquanto o erro direto chega a . Para a cota dá , que o erro real respeita com muita folga.
O próprio dado já vem arredondado: cinco desses coeficientes inteiros não cabem exatamente num float64. A aritmética exata confirma a sensibilidade. Mudar o coeficiente de de para , uma variação relativa de , manda raízes para . Nenhum algoritmo que receba coeficientes arredondados consegue desfazer isso. A cura é outra representação: um programa que guarda os fatores não tem problema nenhum para resolver.
Equívocos Comuns e Armadilhas
- "Mal condicionado quer dizer que o algoritmo é ruim." O condicionamento não envolve algoritmo. O Exemplo 3 tem erro reverso minúsculo e erro direto grande.
- "Resíduo pequeno quer dizer resposta precisa." Quer dizer erro reverso pequeno. A precisão depende também de .
- "Uma resposta precisa prova estabilidade reversa." Tome em . O
1.0calculado tem erro direto , mas é a resposta exata só para , um erro reverso relativo de . - "Mais precisão conserta instabilidade." No Exemplo 2 cada dígito decimal extra compra um passo a mais. A cura é a direção para trás.
- "Uma função tem um número de condição." depende do ponto e do que conta como dado: tem em e em .
- "A cota é uma estimativa." O Exemplo 3 mostra contra um valor real de . Ela limita a pior perturbação, e a perturbação real não precisa ser a pior.
Resumo
O número de condição mede quanto o problema amplia perturbações relativas, antes de qualquer algoritmo rodar. Ele exige , e derivabilidade, e depende do que se toma como dado. O erro direto compara a resposta com a verdade, e o erro reverso pergunta que problema próximo foi resolvido exatamente. Em primeira ordem, o erro direto é no máximo vezes o erro reverso. Um algoritmo reversamente estável num problema bem condicionado dá uma resposta precisa. Num problema mal condicionado, dá erro reverso pequeno e talvez uma resposta ruim, e um algoritmo melhor não muda isso. Quando o erro direto passa muito de , a culpa é da fórmula, muitas vezes por um único passo mal condicionado, e reescrevê-la ou rodá-la em outra direção pode recuperar os dígitos.
Documentation Links
- Driscoll and Braun: Fundamentals of Numerical Computation, 1.2 Problems and conditioning
- Driscoll and Braun: Fundamentals of Numerical Computation, 1.4 Stability
- MIT OpenCourseWare 18.335J (2019): Week 2
- Goldberg (1991): What Every Computer Scientist Should Know About Floating-Point Arithmetic
- NumPy documentation: numpy.roots
- SciPy documentation: scipy.integrate.quad
- Python documentation: math