Use o botão abaixo para reportar erros ou dar sugestões.

Computação Científica - Um Livro Colaborativo

6.3 Problemas


Você deve ter observado que todos os exercícios na aula 8 resultam em um sistema tridiagonal. O algorítmo de Thomas é um método eficiente para resolvê-los.

Exemplo 6.3.1. Implemente o método de Thomas para resolver um sistema Ax = b de dimensão N.

O algoritmo TDMA para resolver o sistema b1 c1 a2 b2 c2 a3 b3 ⋱ ⋱ ⋱ c n−1 an bn x1 x2 x3 ⋮ x n = d1 d2 d3 ⋮ d n .

é dado por ci′ = ci bi, i = 1 ci bi−aici−1′,i = 2, 3,…,n − 1 , di′ = di bi , i = 1 di−aidi−1′ bi−aici−1′,i = 2, 3,…,n.

e xn = dn′ xi = di′ − c i′x i+1,i = n − 1,n − 2,…, 1.

#include <stdio.h>  
#include <math.h>  
#define N 3  
 
//Método de Thomas para resolver Ax=y  
//x entra o vetor y e saí a solução x (N posições, i=0,....,N-1)  
//a entra a subdiagonal à esquerda de A (N-1 posições, i=1,...,N-1)  
//b entra a diagonal  (N posições, i=0,....,N-1)  
//c entra a subdiagonal à direita de A (N-1 posições, i=0,...,N-2)  
void Thomas(double x[], const double a[], const double b[], double c[])  
{  
  int i;  
 
  /*calculo de c[0]’ e d[0]’*/  
  c[0] = c[0] / b[0];  
  x[0] = x[0] / b[0];  
 
 
  /* laço para calcular c’ e d’ */  
  for (i = 1; i < N; i++)  
  {  
    double aux = 1.0/ (b[i] - a[i] * c[i - 1]);  
    c[i] = c[i] * aux;  
    x[i] = (x[i] - a[i] * x[i - 1]) * aux;  
  }  
 
  /* Calculando a solução */  
  for (i = N - 1; i >= 0; i--)  
    x[i] = x[i] - c[i] * x[i + 1];  
}  
 
 
main (void)  
{  
  double x[N],a[N],b[N],c[N];  
  a[0]=0;c[N-1]=0;  
 
  x[0]=4;x[1]=9;x[2]=11;  
  a[1]=1;a[2]=1;  
  b[0]=3;b[1]=3;b[2]=3;  
  c[0]=1;c[1]=1;  
  Thomas(x,a,b,c);  
  printf("%f, %f, %f\n",x[0],x[1],x[2]);  
}

Exemplo 6.3.2. Use o algoritmo TDMA para resolver o PVC − uxx + ux = 200e−(x−1)2,0 < x < 1. 15u(0) + u′(0) = 500 10u(1) + u′(1) = 1

Uma versão discreta oriunda do método de diferenças finitas é 15u1 + u2 − u1 h = 500 − ui+1 + 2ui − ui−1 h2 + ui+1 − ui−1 2h = 200e−(xi−1)2 ,i = 2,3,...,N − 1 10uN + uN − uN−1 h = 1

onde xi = h(i − 1), i = 1,2,...,N, h = 1 N−1. Isto é, u1 15 − 1 h + u2 1 h = 500 ui−1 − 1 h2 − 1 2h + ui 2 h2 + ui+1 − 1 h2 + 1 2h = 200e−(xi−1)2 ,i = 2,3,...,N − 1 uN−1 −1 h + uN 10 + 1 h = 1

ou ainda

15 − 1 h 1 h 0 0 0 ⋯ 0 − 1 h2 − 1 2h 2 h2 − 1 h2 + 1 2h 0 0 ⋯ 0 0 − 1 h2 − 1 2h 2 h2 − 1 h2 + 1 2h 0 ⋯ 0 ⋮⋮⋮⋮⋱⋮ 0 ⋯ 0 0 − 1 h2 − 1 2h 2 h2 − 1 h2 + 1 2h 0 ⋯ 0 0 0 − 1 h 10 + 1 h u1 u2 u3 ⋮ u N−1 uN = 500 200e−(x2−1)2 200e−(x3−1)2 ⋮ 200e−(xN−1−1)2 1
#include <stdio.h>  
#include <math.h>  
#define N 11  
 
//Método de Thomas para resolver Ax=y  
//x entra o vetor y e saí a solução x (N posições, i=0,....,N-1)  
//a entra a subdiagonal à esquerda de A (N-1 posições, i=1,...,N-1)  
//b entra a diagonal  (N posições, i=0,....,N-1)  
//c entra a subdiagonal à direita de A (N-1 posições, i=0,...,N-2)  
void Thomas(double x[], const double a[], const double b[], double c[])  
{  
  int i;  
 
  /*calculo de c[0]’ e d[0]’*/  
  c[0] = c[0] / b[0];  
  x[0] = x[0] / b[0];  
 
 
  /* laço para calcular c’ e d’ */  
  for (i = 1; i < N; i++)  
  {  
    double aux = 1.0/ (b[i] - a[i] * c[i - 1]);  
    c[i] = c[i] * aux;  
    x[i] = (x[i] - a[i] * x[i - 1]) * aux;  
  }  
 
  /* Calculando a solução */  
  for (i = N - 1; i >= 0; i--)  
    x[i] = x[i] - c[i] * x[i + 1];  
}  
 
 
main (void)  
{  
  double x[N],a[N],b[N],c[N];  
  a[0]=0;c[N-1]=0;  
 
  //malha  
  int i;  
  double h=1./(N-1);  
  double p[N];  
  for (i=0;i<N;i++) p[i]=i*h;  
 
  //sistema  
  b[0]=15-1/(h);  
  c[0]=1/(h);  
  x[0]=500;  
  for (i=1;i<N-1;i++)  
  {  
  b[i]=2/(h*h);  
  a[i]=-1/(h*h)-1/(2*h);  
  c[i]=-1/(h*h)+1/(2*h);  
  x[i]=200*exp(-(p[i]-1)*(p[i]-1));  
  }  
  b[N-1]=10+1/h;  
  a[N-1]=-1/h;  
  x[N-1]=1;  
  Thomas(x,a,b,c);  
  for (i=0;i<N;i++) printf("%f\n",x[i]);  
}

Com o objetivo de resolver alguns problemas de contorno não lineares, vamos implementar o método de Newton para sistema.

Exemplo 6.3.3. Implemente um código para calcular raízes do sistema não linear x12 = cos(x 1x2) + 1 sen (x2) = 2 cos(x1).

Calcule uma aproximação para a raíz que fica próxima ao ponto x1 = 1,5 e x2 = 0,5.

O método iterativo de Newton-Raphson para encontrar as raízes de F(x) = 0, F(x) = f1(x1,x2,…,xn) f2(x1,x2,…,xn) ⋮ fn(x1,x2,…,xn) ,

é dado por

x(0) = dado inicial x(k+1) = x(k) − J F −1 x(k) F(x(k)),k ≥ 0

onde a matriz Jacobina JF é JF = ∂(f1,f2,…,fn) ∂(x1,x2,…,xn) = ∂f1 ∂x1 ∂f1 ∂x2 ⋯ ∂f1 ∂xn ∂f2 ∂x1 ∂f2 ∂x2 ⋯ ∂f2 ∂xn ⋮ ⋮ ⋱ ⋮ ∂fn ∂x1 ∂fn ∂x2 ⋯ ∂fn ∂xn .

Especificamente no problema do exemplo 6.3.3, temos F(x) = x12 − cos(x 1x2) − 1 sen (x2) − 2 cos(x1) .

e JF (x) = 2x1 + x2 sen (x1x2) x1 sen (x1x2) 2 sen (x1) cos(x2) .

Observamos que na prática não invertemos a matriz Jacobina, mas calculamos a solução do sistema JF δ = F:

x(0) = 1,5 0,5 JF (x(k))δ(k) = F(x(k))(Calcula-se a solução δ(k) do sistema) x(k+1) = x(k) − δ(k),k ≥ 0
#include <stdio.h>  
#include <math.h>  
#define N 2  
 
//Método de Thomas para resolver Ax=y  
//x entra o vetor y e saí a solução x (N posições, i=0,....,N-1)  
//a entra a subdiagonal à esquerda de A (N-1 posições, i=1,...,N-1)  
//b entra a diagonal  (N posições, i=0,....,N-1)  
//c entra a subdiagonal à direita de A (N-1 posições, i=0,...,N-2)  
void Thomas(double x[], const double a[], const double b[], double c[])  
{  
  int i;  
 
  /*calculo de c[0]’ e d[0]’*/  
  c[0] = c[0] / b[0];  
  x[0] = x[0] / b[0];  
 
 
  /* laço para calcular c’ e d’ */  
  for (i = 1; i < N; i++)  
  {  
    double aux = 1.0/ (b[i] - a[i] * c[i - 1]);  
    c[i] = c[i] * aux;  
    x[i] = (x[i] - a[i] * x[i - 1]) * aux;  
  }  
 
  /* Calculando a solução */  
  for (i = N - 1; i >= 0; i--)  
    x[i] = x[i] - c[i] * x[i + 1];  
}  
//Função  
//x é o ponto de entrada  
//f é o vetor com o resultado de f(x)  
void F(double *x,double *f)  
{  
  f[0]=x[0]*x[0]-cos(x[0]*x[1])-1.0;  
  f[1]=sin(x[1])-2*cos(x[0]);  
}  
//Jacobiana  
//x é o ponto de entrada  
//a sai a subdiagonal à esquerda de A (N-1 posições, i=1,...,N-1)  
//b sai a diagonal  (N posições, i=0,....,N-1)  
//c sai a subdiagonal à direita de A (N-1 posições, i=0,...,N-2)  
void J(double *x,double *a,double *b,double *c)  
{  
  b[0]=2*x[0]+x[1]*sin(x[0]*x[1]);  
  a[0]=0;  
  c[0]=x[0]*sin(x[0]*x[1]);  
  a[1]=2*sin(x[0]);  
  c[1]=0;  
  b[1]=cos(x[1]);  
}  
//norma da diferença entre dois vetores  
double norma_dif(double x[N],double y[N])  
{  
  int i;  
  double norma2=0;  
  for (i=0;i<N;i++)  
  {  
    norma2+=(x[i]-y[i])*(x[i]-y[i]);  
  }  
  return sqrt(norma2);  
}  
main (void)  
{  
  int i;  
  double x0[N],x[N],d[N],a[N],b[N],c[N],tol=1e-10,*px0=x0,*px=x,*paux=NULL;  
  x0[0]=1.5;  
  x0[1]=0.5;  
  do  
  {  
    F(px0,d);  
    J(px0,a,b,c);  
    Thomas(d,a,b,c);  
    for (i=0;i<N;i++) *(px+i)=*(px0+i)-d[i];  
    for (i=0;i<N;i++) printf("%.12f  ",*(px+i));  
    printf("\n");  
    paux=px0;  
    px0=px;  
    px=paux;  
  }while(norma_dif(px0,px)>tol);  
}

Exemplo 6.3.4. Use o método de diferenças finitas e o método de Newton para resolver o problema de valor de contono não-linear − uxx + ux = e−u2,0 < x < 1. u′(0) + 3u(0) = 10 u(1) = 5

Uma versão discreta oriunda do método de diferenças finitas é 3u1 + u2 − u1 h = 10 − ui+1 + 2ui − ui−1 h2 + ui+1 − ui−1 2h = e−ui2 ,i = 2,3,...,N − 1 uN = 5

onde xi = h(i − 1), i = 1,2,...,N, h = 1 N−1. Podemos colocar na forma F(u) = 0, onde u = [u1u2⋯uN]T e

F(u) = 3u1 + u2−u1 h − 10 −ui+1+2ui−ui−1 h2 + ui+1−ui−1 2h − e−ui2,i = 2,3,...,N − 1 uN − 5

A jacobina desse sistema não linear é

JF (u) = 3 − 1 h 1 h 0 0 0 ⋯ 0 − 1 h2 − 1 2h 2 h2 + 2uie−ui2 − 1 h2 + 1 2h 0 0 ⋯ 0 0 − 1 h2 − 1 2h 2 h2 + 2uie−ui2 − 1 h2 + 1 2h 0 ⋯ 0 ⋮ ⋮ ⋮ ⋮ ⋱ ⋮ 0 ⋯ 0 0 − 1 h2 − 1 2h 2 h2 + 2uie−ui2 − 1 h2 + 1 2h 0 ⋯ 0 0 0 0 1

Vamos implementar um código usando o método de Thomas para resolver os sistemas tridiagonais.

#include <stdio.h>  
#include <math.h>  
#define N 11  
#define h (1./(N-1))  
 
//Método de Thomas para resolver Ax=y  
//x entra o vetor y e saí a solução x (N posições, i=0,....,N-1)  
//a entra a subdiagonal à esquerda de A (N-1 posições, i=1,...,N-1)  
//b entra a diagonal  (N posições, i=0,....,N-1)  
//c entra a subdiagonal à direita de A (N-1 posições, i=0,...,N-2)  
void Thomas(double x[], const double a[], const double b[], double c[])  
{  
  int i;  
 
  /*calculo de c[0]’ e d[0]’*/  
  c[0] = c[0] / b[0];  
  x[0] = x[0] / b[0];  
 
 
  /* laço para calcular c’ e d’ */  
  for (i = 1; i < N; i++)  
  {  
    double aux = 1.0/ (b[i] - a[i] * c[i - 1]);  
    c[i] = c[i] * aux;  
    x[i] = (x[i] - a[i] * x[i - 1]) * aux;  
  }  
 
  /* Calculando a solução */  
  for (i = N - 1; i >= 0; i--)  
    x[i] = x[i] - c[i] * x[i + 1];  
}  
//Função  
//x é o ponto de entrada  
//f é o vetor com o resultado de f(x)  
void F(double *x,double *f)  
{  
  int i;  
  f[0]=3*x[0]+(x[1]-x[0])/h-10;  
  for (i=1;i<N-1;i++) f[i]=(-x[i+1]+2*x[i]-x[i-1])/(h*h)+(x[i+1]-x[i-1])/(2*h)-exp(-x[i]*x[i]);  
  f[N-1]=x[N-1]-5;  
}  
//Jacobiana  
//x é o ponto de entrada  
//a sai a subdiagonal à esquerda de A (N-1 posições, i=1,...,N-1)  
//b sai a diagonal  (N posições, i=0,....,N-1)  
//c sai a subdiagonal à direita de A (N-1 posições, i=0,...,N-2)  
void J(double *x,double *a,double *b,double *c)  
{  
  int i;  
  a[0]=0;  
  b[0]=3.-1./h;  
  c[0]=1./h;  
  for (i=1;i<N-1;i++)  
  {  
    a[i]=-1./(h*h)-1./(2*h);  
    b[i]=2./(h*h)+2*x[i]*exp(-x[i]*x[i]);  
    c[i]=-1./(h*h)+1./(2*h);  
  }  
  a[N-1]=0;  
  b[N-1]=1.;  
  c[N-1]=0;  
}  
//norma da diferença entre dois vetores  
double norma_max(double x[N])  
{  
  int i;  
  double norm=fabs(x[0]);  
  for (i=0;i<N;i++) if(norm<fabs(x[i])) norm=fabs(x[i]);  
  return norm;  
}  
void main (void)  
{  
  int i;  
  double x0[N],x[N],d[N],a[N],b[N],c[N],tol=1e-10,*px0=x0,*px=x,*paux=NULL;  
  for (i=0;i<N;i++) x0[i]=0;  
  do  
  {  
    F(px0,d);  
    J(px0,a,b,c);  
    Thomas(d,a,b,c);  
    for (i=0;i<N;i++) *(px+i)=*(px0+i)-d[i];  
    for (i=0;i<N;i++) printf("%.12f  ",*(px+i));  
    printf("\n");  
    paux=px0;  
    px0=px;  
    px=paux;  
  }while(norma_max(d)>tol);  
  for (i=0;i<N;i++) printf("%.12f  ",*(px+i));  
}

Creative Commons License Este texto é disponibilizado nos termos da licença Creative Commons Atribuição-CompartilhaIgual 3.0 Não Adaptada (CC-BY-SA 3.0). Página gerada em 19/8/2020 às 17:33:16.

Informe erros ou edite você mesmo!