Processing math: 100%

Programação numérica

Cálculo de uma série

Cálculo de uma série

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.

Soma truncada

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.

Calcular os termos da série

Sn=1+x+x22!++xnn!=x00!termo 0+x11!termo 1+x22!termo 2++xnn!termo n=T0+T1+T2++TnTk=xkk!

Calcular os termos da série

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

Implementação

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;
}

Testar a função

#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;
}

Execução

$ 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.

Outros critérios de paragem

Segunda versão

double 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;
}

Exemplo de uso

#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;
}

Execução

$ gcc -Wall -o serie serie.c -lm
$ ./serie 
S1=2.7180555556
S2=2.7182815256
e =2.7182818285

Cálculo de potências

Cálculo de potências

Solução simples

xn=x×x××xn 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;
}

Exemplo

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

Exemplo (2)

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 n0.

Propriedades de potências

34=322=(32)2=(33)2

Mais geralmente, para expoentes pares:

x2n=(x2)n=(xx)n

Propriedades de potências (2)

35=31+4=334=3322=3(32)2=3(33)2

Mais geralmente, para expoentes ímpares:

x2n+1=xx2n=x(xx)n

Propriedades de potências (3)

Combinando os dois casos:

x2n=(xx)nx2n+1=x(xx)n

Temos ainda:

x0=1

Estas propriedades dão-nos um algoritmo recursivo para calcular xn.

Algoritmo recursivo

P(x,0)=1P(x,n)=P(xx,n2),(se n>0 e par)P(x,n)=xP(xx,n12),(se n>0 e impar)

Implementação

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);
}

Análise

Quantas multiplicações para calcular xn?

Análise

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:

Otimização

Implementação otimizada

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;
}