1.2.4 Exponenciação
Considere o problema de calcular a exponencial de um número dado. Gostaríamos de uma função que recebe como argumentos uma base e um expoente inteiro positivo e calcula . Uma maneira de fazer isso é através da definição recursiva
que se traduz prontamente na função
Carregando playground de código...
Este é um processo recursivo linear, que requer passos e espaço. Assim como com o fatorial, podemos prontamente formular uma iteração linear equivalente:
Carregando playground de código...
Esta versão requer passos e espaço.
Podemos calcular exponenciais em menos passos usando sucessivas elevações ao quadrado (successive squaring). Por exemplo, em vez de calcular como
podemos calculá-lo usando três multiplicações:
Este método funciona bem para expoentes que são potências de 2. Também podemos tirar proveito de sucessivas elevações ao quadrado no cálculo de exponenciais em geral se usarmos a regra
Podemos expressar este método como uma função:1
Carregando playground de código...
onde o predicado para testar se um inteiro é par é definido em termos do operador %, que calcula o resto após a divisão de inteiros, por
Carregando playground de código...
O processo evoluído por fast_expt cresce logaritmicamente com tanto em espaço quanto em número de passos. Para ver isso, observe que calcular usando fast_expt requer apenas uma multiplicação a mais do que calcular . O tamanho do expoente que podemos calcular, portanto, dobra (aproximadamente) com cada nova multiplicação que nos é permitida. Assim, o número de multiplicações necessárias para um expoente de cresce aproximadamente tão rápido quanto o logaritmo de na base 2. O processo tem crescimento .2
A diferença entre crescimento e crescimento torna-se impressionante à medida que se torna grande. Por exemplo, fast_expt para requer apenas 14 multiplicações.3 Também é possível usar a ideia de sucessivas elevações ao quadrado para desenvolver um algoritmo iterativo que calcula exponenciais com um número logarítmico de passos (veja o exercício 1.16), embora, como é frequentemente o caso com algoritmos iterativos, este não seja escrito tão diretamente quanto o algoritmo recursivo.
Exercício 1.16
Projete uma função que evolui um processo de exponenciação iterativo que usa sucessivas elevações ao quadrado e usa um número logarítmico de passos, assim como fast_expt. (Dica: Usando a observação de que , mantenha, junto com o expoente e a base , uma variável de estado adicional , e defina a transformação de estado de tal forma que o produto não mude de estado para estado. No início do processo, é tomado como 1, e a resposta é dada pelo valor de no final do processo. Em geral, a técnica de definir uma quantidade invariante que permanece inalterada de estado para estado é uma maneira poderosa de pensar sobre o projeto de algoritmos iterativos.)
Exercício 1.17
Os algoritmos de exponenciação nesta seção são baseados em realizar exponenciação por meio de multiplicação repetida. De maneira similar, pode-se realizar a multiplicação de inteiros por meio de adição repetida. O seguinte algoritmo de multiplicação (no qual assumimos que nossa linguagem só pode adicionar, não multiplicar) é análogo à função expt:
function times(a, b) {
return b === 0
? 0
: a + times(a, b - 1);
}
Este algoritmo leva um número de passos que é linear em b. Agora suponha que incluímos, junto com adição, as operações double, que dobra um inteiro, e halve, que divide um inteiro (par) por 2. Usando estas, projete um algoritmo de multiplicação análogo a fast_expt que usa um número logarítmico de passos.
Exercício 1.18
Usando os resultados dos exercícios 1.16 e 1.17, desenvolva uma função que gera um processo iterativo para multiplicar dois inteiros em termos de adicionar, dobrar e dividir pela metade e usa um número logarítmico de passos.
Exercício 1.19
Existe um algoritmo inteligente para calcular os números de Fibonacci em um número logarítmico de passos. Lembre-se da transformação das variáveis de estado e no processo fib_iter da seção 1.2.2: e . Chame essa transformação de , e observe que aplicar vezes, começando com 1 e 0, produz o par Fib() e Fib(). Em outras palavras, os números de Fibonacci são produzidos aplicando , a -ésima potência da transformação , começando com o par (1,0). Agora considere como o caso especial de e em uma família de transformações , onde transforma o par de acordo com e . Mostre que se aplicarmos tal transformação duas vezes, o efeito é o mesmo que usar uma única transformação da mesma forma, e calcule e em termos de e . Isso nos dá um meio explícito de elevar ao quadrado essas transformações, e assim podemos calcular usando sucessivas elevações ao quadrado, como no método fast_expt. Coloque tudo isso junto para completar a seguinte função, que executa em um número logarítmico de passos:
function fib(n) {
return fib_iter(1, 0, 0, 1, n);
}
function fib_iter(a, b, p, q, count) {
return count === 0
? b
: is_even(count)
? fib_iter(a,
b,
⟨??⟩, // calcular p'
⟨??⟩, // calcular q'
count / 2)
: fib_iter(b * q + a * q + a * p,
b * p + a * q,
p,
q,
count - 1);
}
Notas de Rodapé
1 Mais precisamente, o número de multiplicações necessárias é igual a 1 a menos que o logaritmo na base 2 de , mais o número de uns na representação binária de . Este total é sempre menor que o dobro do logaritmo na base 2 de . As constantes arbitrárias e na definição de notação de ordem implicam que, para um processo logarítmico, a base na qual os logaritmos são tomados não importa, então todos esses processos são descritos como .
2 Você pode se perguntar por que alguém se importaria em elevar números à 1000ª potência. Veja a seção 1.2.6.
3 Este algoritmo iterativo é antigo. Ele aparece no Chandah-sutra de Āchārya Piṅgala, escrito antes de 200 a.C. Veja Knuth (1997b), seção 4.6.3, para uma discussão completa e análise deste e outros métodos de exponenciação.
📝 Encontrou algo errado nesta página?
Sua ajuda é muito importante para melhorar a qualidade da tradução!
🐛 Encontrou um erro?
Se você encontrou:
- Erro de tradução (palavra incorreta, termo técnico errado)
- Erro de ortografia ou gramática
- Link quebrado
- Código de exemplo que não funciona
- Problema de formatação
❓ Tem uma dúvida?
Se você tem:
- Dúvida sobre o conteúdo desta seção
- Pergunta sobre um conceito do SICP
- Dificuldade em entender algum exemplo
- Questão sobre a tradução de algum termo
💡 Tem uma sugestão de melhoria?
Se você quer sugerir:
- Melhoria na explicação
- Exemplo adicional
- Recurso visual (diagrama, ilustração)
- Qualquer outra ideia
🌍 Quer discutir a tradução?
Se você quer debater:
- Escolha de tradução de algum termo
- Consistência de terminologia
- Nuances do português
Obrigado por ajudar a melhorar o SICP.js PT-BR! ✨