Podemos exprimir a função exponencial no ponto x como uma soma infinita: ex=1+x+x22!+⋯+xnn!+⋯, onde n! é o fatorial de n.
Vamos calcular ex aproximamente somando um número finito de termos da série acima.
Quanto mais termos somarmos, melhor será a aproximação.
Seja Sn a soma dos termos até ordem n da série: Sn=1+x+x22!+⋯+xnn!
Vamos definir uma função
double soma_termos(double x, int n);
que calcula e retorna a soma Sn para x e n dados.
Sn=1+x+x22!+⋯+xnn!=x00!⏟termo 0+x11!⏟termo 1+x22!⏟termo 2+⋯+xnn!⏟termo n=T0+T1+T2+⋯+TnTk=xkk!
O primeiro termo é: T0=x00!=1
Podemos obter o termo seguinte Tk+1 a partir de Tk sem calcular potências ou fatoriais: Tk+1=Tk×xk+1
double soma_termos(double x, int n) {
double soma = 0.0; /* acumulador */
double termo = 1.0; /* termo inicial */
for (int i = 0; i <= n; i++) {
soma = soma + termo;
termo = termo * x/(i+1);
/* atualizar termo i -> termo i+1 */
/* NB: double/int -> double */
}
return soma;
}
#include <stdio.h>
#include <math.h> // biblioteca matemática
int main(void) {
printf("S5 = %.15f\n", soma_termos(1.0,5));
printf("S10= %.15f\n", soma_termos(1.0,10));
printf("S20= %.15f\n", soma_termos(1.0,20));
printf("e = %.15f\n", exp(1.0));
// função biblioteca
return 0;
}
$ gcc -Wall -o serie serie.c -lm
$ ./serie
S5 = 2.716666666666666
S10 = 2.718281801146385
S20 = 2.718281828459046
e = 2.718281828459045
A opção -lm
indica ao compilador para ligar com a biblioteca de funções matemáticas (exp
, sin
, cos
, fabs
, etc.).
NB: no GCC em Intel X86 podemos omitir esta opção.
soma_termos
necessitamos de saber do número de termos a somardouble soma_eps(double x, double eps) {
double soma = 0.0; // soma acumulada
double termo = 1.0; // termo inicial
for(int i = 0; fabs(termo) > eps; i++) {
// fabs(...): o valor absoluto
soma = soma + termo;
termo = termo * x/(i+1);
}
return soma;
}
#include <stdio.h>
#include <math.h>
int main(void) {
printf("S1=%.10f\n", soma_eps(1.0, 1E-3));
printf("S2=%.10f\n", soma_eps(1.0, 1E-6));
printf("e=%.10f\n", exp(1.0));
/* função da biblioteca math */
return 0;
}
$ gcc -Wall -o serie serie.c -lm
$ ./serie
S1=2.7180555556
S2=2.7182815256
e =2.7182818285
double potencia(double x, int n);
xn=x×x×⋯×x⏟n fatores
double potencia(double x, int n) {
double r = 1.0; /* resultado */
/* repetir n vezes;
i: contador de iterações */
for (int i=0; i<n; i++) {
r = r*x;
}
return r;
}
Quantas multiplicações para calcular potencia(3.0, 4)
?
x = 3.0;
r = 1.0;
r = r*x; // 3.0
r = r*x; // 9.0
r = r*x; // 27.0
r = r*x; // 81.0
Podemos calcular 34 usando apenas duas multiplicações:
x = 3.0; // x é 3.0
x = x*x; // x é 9.0
x = x*x; // x é 81.0
Vamos generalizar esta solução para qualquer n≥0.
34=32⋅2=(32)2=(3⋅3)2
Mais geralmente, para expoentes pares:
x2n=(x2)n=(x⋅x)n
35=31+4=3⋅34=3⋅32⋅2=3⋅(32)2=3⋅(3⋅3)2
Mais geralmente, para expoentes ímpares:
x2n+1=x⋅x2n=x⋅(x⋅x)n
Combinando os dois casos:
x2n=(x⋅x)nx2n+1=x⋅(x⋅x)n
Temos ainda:
x0=1
Estas propriedades dão-nos um algoritmo recursivo para calcular xn.
P(x,0)=1P(x,n)=P(x⋅x,n2),(se n>0 e par)P(x,n)=x⋅P(x⋅x,n−12),(se n>0 e impar)
double potencia(double x, int n) {
if(n == 0)
return 1.0; /* caso base */
else if(n%2 == 0)
/* o expoente é par */
return potencia(x*x, n/2);
else
/* o expoente é ímpar */
return x * potencia(x*x, (n-1)/2);
}
Quantas multiplicações para calcular xn?
Quando n aumenta, log2n é muito menor do que n:
n | log2n |
2 | 1 |
8 | 3 |
32 | 5 |
512 | 9 |
1024 | 10 |
Por exemplo: para n=1000 efetuamos:
double potencia(double x, int n) {
double r = 1.0;
while(n > 0) {
if (n%2 != 0)
r = r*x;
x = x*x;
n = n/2;
}
return r;
}