sexta-feira, 29 de julho de 2011

Como funciona a sua calculadora?

O objetivo desta exposição é mostrar como funciona o algoritmo mais utilizado em calculadoras científicas para calcular, principalmente, funções trigonométricas.


Introdução

Certamente calculadoras científicas utilizam métodos numéricos (algoritmos que obtêm um valor aproximado) para retornar valores de funções como seno, cosseno, exponencial, etc. Você já parou para se perguntar que métodos são esses?

Se você já fez um curso básico de Cálculo, sei que a primeira coisa que passou pela sua cabeça neste momento foi Série de Taylor. Aproximar usando Taylor é realmente muito simples; por exemplo, a série do seno é

 

Basta então escolher alguma precisão (em casas decimais, por exemplo) e truncar esta série para obter uma expressão finita. Por exemplo, todos sabemos que, para os nossos professores de física, , que é uma aproximação em Taylor de ordem 1. Você pode escolher qualquer grau e depois calcular o quão boa é esta aproximação, mas isto não nos interessa no momento.

O fato é que sua calculadora provavelmente não usa aproximação em Taylor! Isto pode parecer, sem dúvida, um tanto estranho. Aproximação em Taylor é simples e converge rapidamente. Além do mais, oferece uma expressão polinomial para qualquer uma destas funções. É difícil conceber algo mais simples.



Porém, temos que pensar que o hardware de uma calculadora é muito limitado. Tão limitado que multiplicar dois números pode se tornar uma operação lenta. De fato, multiplicação é uma operação (veja o post sobre Transformada Rápida de Fourier). Isto leva por água a baixo nossa expressão em Taylor, pois temos de realizar muitas multiplicações.

Antes de mais nada, então, é preciso deixar claro que tipo de operação uma calculadora (e, em geral, um computador qualquer) realiza rapidamente.Lembremos que computadores normalmente trabalham com números binários, então vou pensar em notação binária daqui para frente.

As operações mais rápidas que podem ser feitas num computador são:
  1.  Adicionar e subtrair números.
  2. Comparação de números (i.e. decidir se é maior ou menor).
  3. Armazenamento e leitura de números na memória.
  4. Multiplicação por , . Ex.: .
Num computador com pouca capacidade de processamento, o ideal seria obter um algoritmo que utilizasse apenas estas operações. Mas será que isto é possível?

O algoritmo CORDIC

O objetivo agora é apresentar um algoritmo para calcular funções trigonométricas que se utiliza, majoritariamente, das operações rápidas definidas acima. Na verdade, além das operações rápidas, utilizaremos uma única multiplicação! O preço que pagamos pela eficiência é que este algoritmo não possui um formato tão simples quanto uma aproximação em Taylor.

Em 1959, Jack Volder (não consegui descobrir a profissão dele, mas imagino que seja engenheiro) inventou o algoritmo CORDIC. Esta sigla significa COordinate Rotation DIgital Computer.

Assim como boa parte dos algoritmos numéricos, o CORDIC foi desenvolvido por uma razão muito nobre: fins militares. O objetivo era utilizá-lo no sistema de navegação do bombardeiro B-58 Hustler.


Mas chega de história e falemos, de uma vez por todas, de matemática. O mais interessante é que o algoritmo se utiliza apenas de matemática básica (um pouco de matrizes de rotação é necessário) e é geométrico (no sentido em que é possível visualizar o que está acontecendo)!

Como funciona?

Este é meu testemunho pessoal sobre a dedução do algoritmo. Isto significa que vou tentar explicar as coisas do jeito em que eu acho mais natural. Antes de mais nada, vou tentar passar rapidamente a ideia do algoritmo. Mesmo que não fique muito claro agora, volte aqui depois de ler tudo e as coisas ficarão mais claras.

Passos gerais:
  1. Vamos aproximar e ao mesmo tempo.
  2. Podemos considerar o ponto no círculo unitário. 
  3. Objetivo: obter uma sequência de pontos em convergindo para
  4. Vamos assumir que todos os pontos desta sequência estão no círculo unitário. Por que? Pois é fácil ``mover'' pontos no círculo unitário.
Como movemos pontos no círculo unitário? Com matrizes de rotação. Precisaremos apenas de rotações no plano .

Vamos denotar

De acordo com os passos gerais acima, suponhamos que queremos aproximar . Vamos começar com um ponto no círculo unitário, digamos , e tentar obter uma sequência de ângulos (positivos ou negativos) de maneira esperta tais que


Ou seja, vamos movendo (girando) o vetor até se aproximar do ponto .

Photobucket

Talvez você tenha notado algo muito estranho acontecendo aqui. No início havíamos comentado que multiplicar números é uma tarefa penosa para uma calculadora. No entanto, estamos desenvolvendo um algoritmo que tem que multiplicar matrizes! Isto parece muito pior do que antes.

Nem tudo é o que parece. O segredo agora é realizar uma série de truques espertos de forma que esta multiplicação de matrizes fique muito simples. Para isto, comece observando que podemos fatorar :


O objetivo desta fatoração é tentar simplificar a forma da matriz. Agora pense: o que sabemos multiplicar rapidamente? Potências de 2! Nada mais natural do que considerar apenas ângulos de forma que , (esta é a nossa escolha esperta de ângulos).

Definindo , , temos


Esquecendo o por um momento, observe que estamos realizando apenas operações rápidas para multiplicar por um vetor.

Para utilizar estes ângulos numa calculadora, devemos calculá-los previamente, de outra maneira. Vejamos alguns valores iniciais.


Observe que, eventualmente, podemos parar de calcular estes valores pois , para pequeno. Por exemplo, para (isto é, ), esta aproximação já tem 8 casas decimais de precisão.

Agora expliquemos com um exemplo como que o algoritmo vai funcionar. Suponha que queremos aproximar . Vamos aproximar o ângulo , utilizando nossos . Assim, obteremos uma sequência convergindo para :

Começamos tomando


Muito pequeno, então somamos:


Passou, então subtraímos:


Etc.

Em cada passo, o algoritmo deve verificar se a aproximação obtida é maior ou menor (operação rápida) que o número desejado. Ao mesmo tempo em que aproximamos o ângulo, devemos aproximar o cosseno e o seno. Para isto, vamos multiplicando as matrizes correspondentes.


onde . Fazendo apenas 3 passos, temos , . Enquanto que e .

Então, se o algoritmo rodar passos, teríamos algo do tipo


onde .

Em linhas gerais, este é o algoritmo: aproximar o ângulo somando ou subtraindo ângulos e realizar os produtos das matrizes correspondentes. Mas ainda restaram alguns problemas:
  1. Quem garante que o método sempre converge?
  2. Que critério de parada devemos utilizar?
  3. Como lidar com os ?
Todas estes problemas estão intimamente relacionados. Aqui entra o pulo do gato.

Teorema.  Utilizando o método descrito anteriormente, temos que


se .

Vamos entender como utilizar o teorema acima para resolver todos os nossos problemas em uma tacada só. O teorema acima nos diz de maneira precisa como estimar o erro das aproximações. Por exemplo, se quisermos uma aproximação com 10 casas decimais, poderíamos tomar e parar de aproximar sempre em , pois


Esta observação resolve tudo. Se fixarmos que vamos parar sempre em (para qualquer escolha de ângulo), teremos que será uma constante (pois cosseno independe do sinal [*]). Utilizando qualquer outro método, calculamos previamente:


Feito isto, podemos implementar esta constante direto no hardware, sem precisar calculá-la durante o método. Logo a única operação não-rápida que estamos fazendo é uma multiplicação por uma constante no final. Observe que, como os também já estão pré-definidos, então podemos também implementá-los direto no hardware.

Em nenhum momento eu demonstrei o teorema. Não irei demonstrar aqui pois a demonstração é um pouco técnica e meio chata. No entanto, se você tiver curiosidade, pode clicar aqui. Nada mais além de um primeiro curso de Cálculo é
necessário para compreender esta demonstração.

Observações finais

O mais impressionante é que este mesmo algoritmo pode ser adaptado para calcular qualquer função de uma calculadora científica comum:
  • Funções hiperbólicas;
  • exponencial;
  • logaritmo;
  • raízes.
Porém, os detalhes são mais técnicos e algumas adaptações são até proprietárias. Vasculhando no Google é possível achar algumas coisas.

Este é um belo exemplo de como utilizar pouca matemática mas muita criatividade para criar um algoritmo extremamente útil. Vi inicialmente no artigo Alan Sultan, CORDIC: How Hand Calculators Calculate, The College Mathematics Journal (MAA), Vol. 40, no 2, 2009, pg. 87-92. No link da Wikipedia você acha algumas outras informações interessantes.

Até.


Notas:

[*] Isto mostra que devíamos ter fatorado exatamente o cosseno, e não o seno.

P.S. Segundo o Gabriel, este post não deveria se enquadrar na seção de matemática, pois não demonstrei nenhum teorema. Peço desculpa aos matemáticos que leram este post. :)




2 comentários: