5.6 Integração Numérica — Quadratura de Gauss
1. Introdução
Consideremos o problema de encontrar uma aproximação numérica para a integral da função linear
onde . Como é linear. É coerente aproximarmos isso por um funcional também linear,
onde é chamada de problema da quadratura. Isto é,
Note que quando , temos a aproximação da integral como uma combinação linear dos valores de e suas derivadas. O problema da quadratura numérica consiste em especificar e tais que esta aproximação obedeçam a certas propriedades, tais como convergência e acurácia.
Nossa abordagem consiste em definir uma aproximação polinomial de de forma a escolher e tais que minimizem para um aproximante de grau suficientemente baixo. Assim como fizemos com a regra do trapézio, representaremos a integral como uma combinação linear de valores funcionais
únicos. Ou seja, reescreveremos a integral como
Vamos assumir que os limites de integração e da equação acima são finitos. Se a equação acima é exata para polinômios de grau ou menores, podemos utilizar um conjunto de equações para descobrir constantes ao substituir para na equação acima. Setando , temos que
para , onde
Seja a fórmula de interpolação de Hermite:
que é exata para os polinômios de grau ou menores. Integrando essa equação, temos
onde
e
Como é zero, se é um polinômio de grau ou menor e se podemos escolher as abcissas tal que , então teremos as propriedades desejadas.
2. Quadratura de Gauss-Legendre
A quadratura de Gauss-Legendre consiste em aproximar a integral utilizando pontos
onde as abcissas são as raízes do polinômio de Legendre e os pesos são obtidos por
3. Implementação
Como podemos notar das seções anteriores, a quadratura requer avaliar a função em pontos bem definidos utilizando pesos específicos. Como dito na última seção, os pontos a serem utilizados são as raízes do polinômio de Legendre. Além disso, podemos ver que os pesos também dependem dessas raízes . Sendo assim, o problema da quadratura de Gauss-Legendre é resolvido em resolver quatro grandes etapas:
- Obter o polinômio de Legendre de ordem , onde é o número de pontos utilizados.
- Encontrar as raízes do polinômio obtido.
- Encontrar a primeira derivada do n-ésimo polinômio, pois o peso é dependente desta função. Com isso podemos determinar os pesos .
- Computar a quadratura gaussiana, utilizando os pesos e as raízes .
3.1. Computando o Polinômio de Legendre
O primeiro passo é encontrar os polinômios de Legendre para que possamos encontrar suas raízes. Como sabemos que nos passos posteriores teremos que encontrar a derivada do n-ésimo polinômio, escolhemos gerar seus coeficientes e colocá-los em uma lista ordenada da maior ordem para a menor. Por exemplo, o polinômio
pode ser representado em forma de lista
onde a ordem fica implícita na posição do vetor em ordem decrescente. Assim, a função que computa os coeficientes, representando o polinômio como uma lista é
def compute_legendre_polynomials_coeficients(n):
"""
Compute the coefficients of the nth Legendre polynomial.
[coefficients] = compute_legendre_polynomials_coeficients(n)
INPUT:
* n: order of the Legendre polynomial
return: a list containing the polynomial coefficients of nth Legendre
polynomial (ordered from the highest to lowest)
Author: Pedro Garcia [sawp@sawp.com.br]
see: http://www.sawp.com.br
License: Creative Commons
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
Jul 2012
"""
b = 2.0 ** n
B = binomial_coefficients
f = lambda k: b * B(n, k) * B((n + k - 1) / 2.0, n)
a = [f(k) for k in xrange(0, n + 1)]
a.reverse()
return a
</p>
Note que esta função requer o cálculo do binômio . A função que faz esta computação é
def binomial_coefficients(n, k):
"""
Calcule any binomial coefficients.
coeff = binomial coefficients(n, k)
INPUT:
* n: power of binomial
* k: coefficient of x^k term
return: binomial coefficient
Author: Pedro Garcia [sawp@sawp.com.br]
see: http://www.sawp.com.br
License: Creative Commons
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
Jul 2012
"""
fun = lambda i: (n - i + 1.0) / i
l = [1.0] + [fun(i) for i in xrange(1, k + 1)]
coeff = reduce(mul, l)
return coeff
</p>
3.2. Computando as Raízes do Polinômio de Legendre
O próximo passo é obter as raízes do polinômio de Legendre. Para isso, precisamos de um algoritmo que calcule as raízes de um polinômio de ordem . Nesse caso, podemos utilizar qualquer método numérico que realize esta tarefa: Aberth, Bairstow, Laguerre, Jenkins-Traub, etc.
Como apresentado em outros posts, cada um desses métodos possui vantagens e desvantagens, tais como requerer ou não derivadas, complexidade, etc. Sabendo que todas as raízes do polinômio de Legendre estão definidas no intervalo e sabendo os coeficientes do polinômio, o que nos permite derivá-lo, escolhemos o método de Aberth. Esse método possui uma convergência quadrática e requer a função (polinômio) e a sua derivada. Assim, podemos computar todas as raízes do polinômio utilizando a seguinte função:
def compute_legendre_roots(n):
"""
Compute all roots of the nth Legendre polynomial
[roots] = compute_legendre_roots(n)
INPUT:
* n: order of Legendre polynomial
return: the roots of P_n(x)
Author: Pedro Garcia [sawp@sawp.com.br]
see: http://www.sawp.com.br
License: Creative Commons
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
Jul 2012
"""
legendre_coefficients = compute_legendre_polynomials_coeficients(n)
diff_coefs = polynomial_derivative_coefficients(legendre_coefficients)
legendre_fun = polynomial_coeffs_to_function(legendre_coefficients)
diff_legendre_fun = polynomial_coeffs_to_function(diff_coefs)
roots = aberth(legendre_fun, diff_legendre_fun, polDegree=n)
return roots
Onde polynomial_derivative_coefficients realiza a derivada de um polinômio representado como lista e retorna o outro polinômio derivado também em forma de lista:
def polynomial_derivative_coefficients(coeff):
"""
Compute the derivative of a polynomial represented by a list.
[diff_coefficients] = polynomial_derivative_coefficients(coeff)
INPUT:
* coeff: coefficients of the polynomial
return: a list containing the polynomial coefficients of the derivated
polynomial (ordered from the highest to lowest)
Author: Pedro Garcia [sawp@sawp.com.br]
see: http://www.sawp.com.br
License: Creative Commons
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
Jul 2012
"""
order = len(coeff) - 1
f = lambda i: (order - i) * coeff[i]
diff = [f(i) for i in xrange(order)]
return diff
</p>
A função polynomial_coeffs_to_function converte uma representação em forma de lista em uma função avaliável em qualquer ponto:
def polynomial_coeffs_to_function(coeff):
"""
Convert the polynomial coefficients in a polynomial function.
function = polynomial_coeffs_to_function(coeff)
INPUT:
* coeff: coefficients of the polynomial
return: a function
Author: Pedro Garcia [sawp@sawp.com.br]
see: http://www.sawp.com.br
License: Creative Commons
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
Jul 2012
"""
order = len(coeff) - 1
f = lambda x: sum([coeff[i] * x ** (order - i) for i in xrange(order + 1)])
return f
</p>
3.3. Computando os Pesos da Quadratura
Conforme a função apresentada, os pesos obtidos a partir das raízes são calculados pela seguinte função:
def compute_quadrature_weights(n):
"""
Compute the weights of each coefficient used in gaussian quadrature.
[weights] = compute_quadrature_weights(n)
INPUT:
* n: order of Legendre polynomial
return: a list with the weights
Author: Pedro Garcia [sawp@sawp.com.br]
see: http://www.sawp.com.br
License: Creative Commons
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
Jul 2012
"""
def get_diff_Legendre_n(n):
l1 = compute_legendre_polynomials_coeficients(n)
diff_coefs = polynomial_derivative_coefficients(l1)
return polynomial_coeffs_to_function(diff_coefs)
def compute_weights(n):
p1n = get_diff_Legendre_n(n)
a = compute_legendre_roots(n)
h = lambda i: 2.0 / ((1.0 - a[i] * a[i]) * p1n(a[i]) * p1n(a[i]))
H = [h(j) for j in xrange(n)]
return H
return compute_weights(n)
</p>
3.4. Computando a Integral
O último passo é computar a integral da função escolhida utilizando a quadratura de Gauss-Legendre:
def integral_gaussian_quadrature(fun, xi, xe, order=10):
"""
Numerical integration using Gaussian quadrature
integral = integral_gaussian_quadrature(fun, xi, xe, order=10)
INPUT:
* fun: function to be integrated
* xi: beginning of integration interval
* xe: end of integration interval
* order: degree of Legendre polynomial
return: \int_{xi}^{xe} f(x)
Author: Pedro Garcia [sawp@sawp.com.br]
see: http://www.sawp.com.br
License: Creative Commons
http://creativecommons.org/licenses/by-nc-nd/2.5/br/
Jul 2012
"""
def convert_intervals():
frac = (xe - xi) / 2.0
xd = lambda x: (xe + xi + (xe - xi) * x) / 2.0
f = lambda x: fun(xd(x)) * frac
return f
def compute_quadrature():
f = convert_intervals()
roots = compute_legendre_roots(order)
weights = compute_quadrature_weights(order)
approx = lambda i: weights[i] * f(roots[i].real)
quad = [approx(i) for i in xrange(order)]
return sum(quad)
integral = compute_quadrature()
return integral
Onde convert_intervals realiza uma mudança de variáveis para converter um intervalo qualquer para o intervalo compatível com o da integral demonstrada no método, isto é, .
Um exemplo de utilização dessas funções pode ser obtido em http://www.sawp.com.br/code/integral/integral_gauss_legendre_quadrature.py.
4. Copyright
Este documento é disponível sob a licença Creative Commons. As regras dos direitos de cópia deste conteúdo estão acessíveis em http://creativecommons.org/licenses/by-nc-nd/2.5/br/.