"""
Módulo para métodos de integração numérica.
Este módulo implementa algoritmos de integração numérica:
Regra dos trapézios, Simpson 1/3 e suas versões repetidas.
Author: Pedro Henrique Rocha de Andrade
Date: Dezembro 2025
"""
import math
import numpy as np
from sympy import symbols, integrate, sympify, diff, lambdify
from sympy import sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, exp, sqrt, log, Abs, pi, E
import matplotlib.pyplot as plt
from .constants import SYMPY_LOCALS
[documentos]
def plotar_funcoes(funcs, a, b, pontos=400):
"""Plota uma ou várias funções simbólicas no intervalo [a, b].
Parameters
----------
funcs : str
String com expressões separadas por vírgula (ex.: ``'sin(x), cos(x)'``).
a, b : float
Limites do intervalo de plotagem.
pontos : int, optional
Número de pontos no eixo x usados para amostragem (padrão: 400).
"""
x = symbols('x')
lista = [f.strip() for f in funcs.split(',') if f.strip()]
xs = np.linspace(a, b, pontos)
plt.figure()
any_plotted = False
for fstr in lista:
try:
f_clean = fstr.replace('math.', '')
fexpr = sympify(f_clean, locals=SYMPY_LOCALS)
ffunc = lambdify(x, fexpr, modules=['numpy'])
ys = ffunc(xs)
plt.plot(xs, ys, label=fstr)
any_plotted = True
except Exception as e:
print(f"Erro ao processar função '{fstr}': {e}")
if not any_plotted:
print("Nenhuma função pôde ser plotada.")
return
plt.axhline(0, color='k', linewidth=0.6)
plt.legend()
plt.xlabel('x')
plt.ylabel('f(x)')
# plt.title('')
plt.grid(True)
plt.show()
[documentos]
def plotar_funcao_e_aproximacao(func, a, b, m, metodo=None, pontos=400):
"""Plota a função e a aproximação composta baseada em nós igualmente espaçados.
Parameters
----------
func : str
Expressão da função em termos de ``x``.
a, b : float
Intervalo da integração.
m : int
Número de subintervalos (m > 0).
metodo : str, optional
Legenda para a aproximação (ex.: ``'Trapézio composta'``).
pontos : int, optional
Número de pontos para plotagem da função exata.
"""
try:
import matplotlib.pyplot as plt
except Exception:
print("matplotlib não está disponível. Instale com 'pip install matplotlib'.")
return
x = symbols('x')
func_clean = func.replace('math.', '')
xs = np.linspace(a, b, pontos)
# tenta criar função vetorizada via sympy
try:
fexpr = sympify(func_clean, locals=SYMPY_LOCALS)
ffunc = lambdify(x, fexpr, modules=['numpy'])
ys = ffunc(xs)
except Exception:
# fallback para eval ponto-a-ponto
ys = []
safe_math = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")}
for xv in xs:
try:
yv = eval(func, {"__builtins__": None}, {"x": xv, "math": math, **safe_math})
except Exception:
yv = float('nan')
ys.append(yv)
ys = np.array(ys, dtype=float)
# calcula nós de aproximação
try:
m_int = int(m)
if m_int <= 0:
raise ValueError("m deve ser positivo")
except Exception:
print("Valor de m inválido para plotar aproximação.")
return
h = (b - a) / m_int
xi = np.array([a + i * h for i in range(m_int + 1)], dtype=float)
# avalia a função nos nós (tenta lambdify, senão eval)
try:
if 'ffunc' in locals():
yi = ffunc(xi)
else:
raise Exception()
except Exception:
yi = []
safe_math = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")}
for xv in xi:
try:
yv = eval(func, {"__builtins__": None}, {"x": xv, "math": math, **safe_math})
except Exception:
yv = float('nan')
yi.append(yv)
yi = np.array(yi, dtype=float)
plt.figure()
plt.plot(xs, ys, label='f(x) exata', linewidth=2)
# desenha a aproximação através dos nós conectados
plt.plot(xi, yi, '--o', color='orange', label=f'Aproximação ({metodo or "composta"})')
# Desenhar polinômios locais usados pela fórmula composta (Lagrange)
def lagrange_eval(x_nodes, y_nodes, x_eval):
x_nodes = np.asarray(x_nodes, dtype=float)
y_nodes = np.asarray(y_nodes, dtype=float)
x_eval = np.asarray(x_eval, dtype=float)
P = np.zeros_like(x_eval, dtype=float)
n = len(x_nodes)
for j in range(n):
# compute L_j(x) basis
lj = np.ones_like(x_eval, dtype=float)
for k in range(n):
if k == j:
continue
lj *= (x_eval - x_nodes[k]) / (x_nodes[j] - x_nodes[k])
P += y_nodes[j] * lj
return P
method_lower = (metodo or '').lower()
# Trapezio: a linha conectando nós já representa o polinômio de grau 1 por segmento
if 'simpson 1/3' in method_lower or 'simpson13' in method_lower:
# para cada par de subintervalos (2 subintervalos, 3 nós), desenha o polinômio quadrático
for i in range(0, m_int, 2):
if i + 2 > m_int:
break
nodes_x = xi[i:i+3]
nodes_y = yi[i:i+3]
xs_local = np.linspace(nodes_x[0], nodes_x[-1], max(50, int(pontos*(len(nodes_x)/len(xs)))))
try:
ys_local = lagrange_eval(nodes_x, nodes_y, xs_local)
plt.plot(xs_local, ys_local, color='red', linewidth=1.8, alpha=0.9)
except Exception:
# ignora se algo falhar na interpolação local
pass
elif 'simpson 3/8' in method_lower or 'simpson38' in method_lower:
# para cada bloco de 3 subintervalos (4 nós), desenha o polinômio cúbico
for i in range(0, m_int, 3):
if i + 3 > m_int:
break
nodes_x = xi[i:i+4]
nodes_y = yi[i:i+4]
xs_local = np.linspace(nodes_x[0], nodes_x[-1], max(60, int(pontos*(len(nodes_x)/len(xs)))))
try:
ys_local = lagrange_eval(nodes_x, nodes_y, xs_local)
plt.plot(xs_local, ys_local, color='green', linewidth=1.8, alpha=0.9)
except Exception:
pass
plt.axhline(0, color='k', linewidth=0.6)
plt.legend()
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Função e aproximação')
plt.grid(True)
plt.show()
[documentos]
def pedir_dados_integral():
"""Lê interativamente a função e limites para integração.
Returns
-------
(func, a, b, composta)
func : str ou None - expressão da função
a, b : float - limites do intervalo
composta : bool - se deve usar regra composta
Retorna (None, None, None, None) em caso de erro de parsing.
"""
func = input("Digite a função f(x) (ex: sin(x), log(x), exp(x), etc): ")
a_str = input("Limite inferior (a): ")
b_str = input("Limite superior (b): ")
try:
a = float(sympify(a_str, locals=SYMPY_LOCALS))
b = float(sympify(b_str, locals=SYMPY_LOCALS))
except Exception as e:
print(f"Erro ao interpretar os limites: {e}")
return None, None, None, None
composta = input("Deseja usar a versão composta? (s/n): ").strip().lower() == 's'
return func, a, b, composta
[documentos]
def pedir_m_ou_h(a, b, regra):
"""Auxiliar que solicita ``m`` (número de subintervalos) ou ``h`` (tamanho do passo).
Parameters
----------
a, b : float
Intervalo de integração.
regra : str
Identificador da regra ('trapezio', 'simpson13', 'simpson38') usado para validar requisitos (paridade, múltiplos).
Returns
-------
(int, float) or (None, None)
Tupla (m, h) calculada, ou (None, None) em caso de entrada inválida.
"""
escolha = input("Deseja informar o número de subintervalos (m) ou o tamanho do passo (h)? [m/h]: ").strip().lower()
if escolha == 'h':
try:
h = float(input("Digite o valor de h (tamanho do passo): "))
if h == 0:
print("O passo h não pode ser zero.")
return None, None
except Exception:
print("Valor de h inválido.")
return None, None
m = (b - a) / h
m_int = int(round(m))
if regra == 'simpson13' and m_int % 2 != 0:
print("Número de subintervalos calculado não é par para Simpson 1/3 composta. Retornando ao menu.")
return None, None
if regra == 'simpson38' and m_int % 3 != 0:
print("Número de subintervalos calculado não é múltiplo de 3 para Simpson 3/8 composta. Retornando ao menu.")
return None, None
m = m_int
if m <= 0:
print("Número de subintervalos inválido calculado (<=0). Retornando ao menu.")
return None, None
h = (b - a) / m
print(f"Número de subintervalos ajustado (m): {m}")
print(f"Tamanho do passo ajustado (h): {h}")
else:
try:
m = int(input("Digite o número de subintervalos (m): "))
if m <= 0:
print("Número de subintervalos deve ser maior que zero. Retornando ao menu.")
return None, None
except Exception:
print("Valor de m inválido. Retornando ao menu.")
return None, None
if regra == 'simpson13' and m % 2 != 0:
print("Número de subintervalos deve ser par para Simpson 1/3 composta. Retornando ao menu.")
return None, None
if regra == 'simpson38' and m % 3 != 0:
print("Número de subintervalos deve ser múltiplo de 3 para Simpson 3/8 composta. Retornando ao menu.")
return None, None
h = (b - a) / m
return m, h
[documentos]
def erro_truncamento_composta(a, b, m, derivada_max, metodo):
"""Estimativa do erro de truncamento para regras compostas.
Parameters
----------
a, b : float
Intervalo de integração.
m : int
Número de subintervalos.
derivada_max : float
Valor máximo estimado da derivada relevante no intervalo (ex.: segunda derivada para trapézio).
metodo : str
'trapezio', 'simpson13' ou 'simpson38'.
Returns
-------
float or None
Estimativa do erro de truncamento (pode ser negativa conforme fórmula) ou None se método desconhecido.
"""
if metodo == 'trapezio':
return -((b-a)**3) / (12 * m**2) * derivada_max
elif metodo == 'simpson13':
return -((b-a)**5) / (180 * m**4) * derivada_max
elif metodo == 'simpson38':
return -((b-a)**5) / (80 * m**4) * derivada_max
else:
return None
[documentos]
def trapezio_tabela():
'''
Regra do Trapézio para dados em tabela (x, y).
Requer que o número de pontos seja pelo menos 2
Fórmula: int_a^b f(x) dx ≈ (h/2) * [f(x0) + f(xn)]
'''
print("\n--- Regra do Trapézio com dados em tabela ---")
n = int(input("Digite o número de pontos (n >= 2): "))
if n < 2:
print("Número de pontos deve ser pelo menos 2.")
return None
x = []
y = []
print("Digite os valores de x em ordem crescente:")
for i in range(n):
x.append(float(input(f"x[{i}]: ")))
print("Digite os valores correspondentes de y = f(x):")
for i in range(n):
y.append(float(input(f"y[{i}]: ")))
integral = 0.0
for i in range(n - 1):
h = x[i+1] - x[i]
integral += (h * (y[i] + y[i+1]) / 2)
print(f"\nResultado da integral pelo Trapézio com dados discretos: {integral}\n")
[documentos]
def simpson_1_3_tabela():
'''
Regra de Simpson 1/3 para dados em tabela (x, y).
Requer que o número de pontos seja ímpar
Fórmula: int_a^b f(x) dx ≈ (h/3) * [f(x0) + 4f(x1) + 2f(x2) + ... + 4f(xn-1) + f(xn)]
'''
print("\n--- Regra de Simpson 1/3 com dados em tabela ---")
n = int(input("Digite o número de pontos (n deve ser ímpar, pelo menos 3): "))
if n < 3 or n % 2 == 0:
print("Número de pontos inválido. Deve ser ímpar e pelo menos 3.")
return
x = []
y = []
print("Digite os valores de x em ordem crescente:")
for i in range(n):
x.append(float(input(f"x[{i}]: ")))
print("Digite os valores correspondentes de y = f(x):")
for i in range(n):
y.append(float(input(f"y[{i}]: ")))
h = (x[-1] - x[0]) / (n - 1)
integral = y[0] + y[-1]
for i in range(1, n-1):
if i % 2 == 0:
integral += 2 * y[i]
else:
integral += 4 * y[i]
integral *= h / 3
print(f"\nResultado da integral pela Regra de Simpson 1/3 com dados discretos: {integral}\n")
[documentos]
def simpson_3_8_tabela():
''''
Regra de Simpson 3/8 para dados em tabela (x, y).
Requer que os intervalos sejam multiplo de 3
Fórmula: int_a^b f(x) dx ≈ (3h/8) * [f(x0) + 3f(x1) + 3f(x2) + 2f(x3) + ... + 3f(xn-1) + f(xn)]
'''
print("\n--- Regra de Simpson 3/8 com dados em tabela ---")
n = int(input("Digite o número de pontos (n deve ser múltiplo de 3 mais 1, ex: 4, 7, 10): "))
if (n - 1) % 3 != 0 or n < 4:
print("Número de pontos inválido. Deve ser 3k + 1 e pelo menos 4.")
return None
x = []
y = []
print("Digite os valores de x em ordem crescente:")
for i in range(n):
x.append(float(input(f"x[{i}]: ")))
print("Digite os valores correspondentes de y = f(x):")
for i in range(n):
y.append(float(input(f"y[{i}]: ")))
h = (x[-1] - x[0]) / (n - 1)
integral = y[0] + y[-1]
for i in range(1, n - 1):
if i % 3 == 0:
integral += 2 * y[i]
else:
integral += 3 * y[i]
integral *= 3 * h / 8
print(f"\nResultado da integral pela Regra de Simpson 3/8 com dados discretos: {integral}\n")
return
[documentos]
def calcular_integral_analitica():
'''
Calcula a integral de uma função simbolicamente usando SymPy.
Requer a função e os limites de integração.
Fórmula: int_a^b f(x) dx
'''
print("\nCálculo Integral")
expressao = input("Digite a função f(x): ")
a_str = input("Limite inferior (a): ")
b_str = input("Limite superior (b): ")
try:
a = float(sympify(a_str, locals=SYMPY_LOCALS))
b = float(sympify(b_str, locals=SYMPY_LOCALS))
except Exception as e:
print(f"Erro ao interpretar os limites: {e}")
return None
x = symbols('x')
try:
funcao = sympify(expressao, locals=SYMPY_LOCALS)
integral_exata = integrate(funcao, (x, a, b))
print(f"\nResultado exato da integral de {expressao} de {a} a {b}: {integral_exata.evalf()}\n")
return integral_exata.evalf()
except Exception as e:
print("\nErro ao calcular a integral simbolicamente:", e)
return None
[documentos]
def newton_cotes(func, a, b, ordem, verbose=False, grafico=None):
"""Newton-Cotes para integração numérica (ordens 1,2,3).
Otimizações
-----------
- Tenta usar `sympy.lambdify` para avaliar a função de forma vetorizada quando possível.
Verbose & Gráficos
------------------
- `verbose=False` (padrão): execução silenciosa (comportamento compatível com testes automatizados).
- `verbose=True`: imprime detalhes auxiliares e, por padrão, habilita `grafico=True`.
Parâmetros
----------
func : str
Expressão da função em termos de ``x``.
a, b : float
Limites do intervalo de integração (números finitos, distintos).
ordem : int
Ordem da regra (1, 2 ou 3).
verbose : bool, optional
Habilita saídas detalhadas e gráficos (padrão: False).
grafico : bool or None, optional
Controla plotagem: se None e ``verbose`` for True, é habilitado; caso contrário, respeitado.
Retorno
-------
float
Aproximação da integral.
Notas de teste
--------------
Os testes unitários que cobrem Newton-Cotes e regras compostas estão em ``tests/test_integracoes.py``.
"""
# Validações básicas de entrada
try:
a = float(a); b = float(b)
except Exception:
raise TypeError("Os limites a e b devem ser numéricos e conversíveis para float.")
if not np.isfinite(a) or not np.isfinite(b):
raise ValueError("Os limites a e b devem ser finitos (não NaN ou infinito).")
if a == b:
raise ValueError("Os limites a e b não podem ser iguais.")
if ordem not in (1, 2, 3):
raise ValueError("Ordem inválida para Newton-Cotes. Deve ser 1, 2 ou 3.")
if grafico is None:
grafico = bool(verbose)
h = (b - a) / ordem
# Tentar avaliação vetorizada com SymPy/lambdify
x_sym = symbols('x')
func_clean = func.replace('math.', '')
try:
fexpr = sympify(func_clean, locals=SYMPY_LOCALS)
fvec = lambdify(x_sym, fexpr, modules=['numpy'])
x = np.linspace(a, b, ordem + 1)
y = np.asarray(fvec(x), dtype=float)
except Exception:
# Fallback ponto a ponto
x = [a + i * h for i in range(ordem + 1)]
y = []
safe_math = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")}
for xi in x:
try:
yi = eval(func, {"__builtins__": None}, {"x": xi, "math": math, **safe_math})
except Exception as e:
if verbose:
print(f"Erro na avaliação da função em x={xi}: {e}")
return None
y.append(yi)
y = np.asarray(y, dtype=float)
if ordem == 1:
resultado = h * (y[0] + y[1]) / 2
metodo_nome = "Regra do Trapézio"
elif ordem == 2:
resultado = (h / 3) * (y[0] + 4 * y[1] + y[2])
metodo_nome = "Regra de Simpson 1/3"
else:
resultado = (3 * h / 8) * (y[0] + 3 * y[1] + 3 * y[2] + y[3])
metodo_nome = "Regra de Simpson 3/8"
if verbose:
print(f"\nResultado pela {metodo_nome}: {float(resultado)}\n")
if grafico:
try:
plotar_funcao_e_aproximacao(func, a, b, ordem, metodo=metodo_nome)
except Exception as e:
if verbose:
print(f"Não foi possível gerar o gráfico: {e}")
return float(resultado)
[documentos]
def trapezio_composta(func, a, b, verbose=False, grafico=None):
"""Regra do Trapézio composta para integração numérica.
Parameters
----------
func : str
Expressão da função em termos de ``x``.
a, b : float
Limites do intervalo de integração.
verbose : bool, optional
Se True, imprime detalhes e habilita (por padrão) a plotagem e estimativa de erro.
grafico : bool or None, optional
Controla plotagem: se None e verbose for True, habilita-a.
Returns
-------
float or None
Aproximação da integral ou ``None`` em caso de erro/entrada inválida.
Notes
-----
Testes para regras compostas estão em ``tests/test_integracoes.py``.
"""
m, h = pedir_m_ou_h(a, b, 'trapezio')
if m is None:
return None
if grafico is None:
grafico = bool(verbose)
# Tentar vetorizar avaliação via sympy
x_sym = symbols('x')
func_clean = func.replace('math.', '')
try:
fexpr = sympify(func_clean, locals=SYMPY_LOCALS)
fvec = lambdify(x_sym, fexpr, modules=['numpy'])
xi = np.linspace(a, b, m + 1)
yi = np.asarray(fvec(xi), dtype=float)
except Exception:
xi = np.array([a + i * h for i in range(m + 1)], dtype=float)
yi = []
safe_math = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")}
for xv in xi:
try:
yv = eval(func, {"__builtins__": None}, {"x": xv, "math": math, **safe_math})
except Exception as e:
if verbose:
print(f"Erro na avaliação da função em x={xv}: {e}")
return None
yi.append(yv)
yi = np.asarray(yi, dtype=float)
# Regra composta
resultado = (h / 2) * (yi[0] + 2 * np.sum(yi[1:-1]) + yi[-1])
if verbose:
print(f"\nResultado pela Regra do Trapézio composta: {float(resultado)}")
print(f"m (subintervalos): {m}, h (passo): {h}")
if grafico:
try:
plotar_funcao_e_aproximacao(func, a, b, m, metodo='Trapézio composta')
except Exception as e:
if verbose:
print(f"Não foi possível gerar o gráfico: {e}")
# Se verbose, estima erro automaticamente; caso contrário, mantém prompts interativos
if verbose:
try:
x = symbols('x')
func_expr = sympify(func_clean, locals=SYMPY_LOCALS)
deriv2 = diff(func_expr, x, 2)
deriv2_func = lambdify(x, deriv2, modules=["numpy"]) # vetoriza com numpy
xs = np.linspace(a, b, 1000)
vals = np.abs(deriv2_func(xs))
derivada_max = float(np.nanmax(vals))
erro = erro_truncamento_composta(a, b, m, derivada_max, 'trapezio')
print(f"Erro de truncamento estimado (Trapézio composta): {erro}")
except Exception as e:
if verbose:
print(f"Não foi possível calcular o erro automaticamente: {e}")
else:
# modo interativo: perguntar ao usuário se deseja plotar e estimar erro (compatibilidade)
try:
if input("Deseja plotar a função e a aproximação no intervalo [a,b]? (s/n): ").strip().lower() == 's':
try:
plotar_funcao_e_aproximacao(func, a, b, m, metodo='Trapézio composta')
except Exception as e:
print(f"Não foi possível gerar o gráfico: {e}")
except EOFError:
# Em ambiente não interativo, ignora
pass
try:
if input("Deseja estimar o erro de truncamento? (s/n): ").strip().lower() == 's':
try:
x = symbols('x')
func_expr = sympify(func_clean, locals=SYMPY_LOCALS)
deriv2 = diff(func_expr, x, 2)
deriv2_func = lambdify(x, deriv2, modules=["numpy"])
xs = np.linspace(a, b, 1000)
vals = np.abs(deriv2_func(xs))
derivada_max = float(np.nanmax(vals))
erro = erro_truncamento_composta(a, b, m, derivada_max, 'trapezio')
print(f"Erro de truncamento estimado (Trapézio composta): {erro}")
except Exception as e:
print(f"Não foi possível calcular o erro automaticamente: {e}")
except EOFError:
pass
print()
return float(resultado)
[documentos]
def simpson_1_3_composta(func, a, b, verbose=False, grafico=None):
"""Regra de Simpson 1/3 composta para integração numérica.
Parameters
----------
func : str
Expressão da função em termos de ``x``.
a, b : float
Limites do intervalo de integração.
verbose : bool, optional
Se True, imprime detalhes, habilita plot e estimativa de erro.
grafico : bool or None, optional
Controla plotagem: se None e verbose for True, habilita-a.
Returns
-------
float or None
Aproximação da integral ou ``None`` em caso de erro/entrada inválida.
"""
m, h = pedir_m_ou_h(a, b, 'simpson13')
if m is None:
return None
if grafico is None:
grafico = bool(verbose)
# Tentar vetorizar avaliação
x_sym = symbols('x')
func_clean = func.replace('math.', '')
try:
fexpr = sympify(func_clean, locals=SYMPY_LOCALS)
fvec = lambdify(x_sym, fexpr, modules=['numpy'])
xi = np.linspace(a, b, m + 1)
yi = np.asarray(fvec(xi), dtype=float)
except Exception:
xi = np.array([a + i * h for i in range(m + 1)], dtype=float)
yi = []
safe_math = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")}
for xv in xi:
try:
yv = eval(func, {"__builtins__": None}, {"x": xv, "math": math, **safe_math})
except Exception as e:
if verbose:
print(f"Erro na avaliação da função em x={xv}: {e}")
return None
yi.append(yv)
yi = np.asarray(yi, dtype=float)
# Aplica a regra composta (Simpson 1/3 exige m par)
resultado = (h / 3) * (yi[0] + yi[-1] + 2 * np.sum(yi[2:-1:2]) + 4 * np.sum(yi[1::2]))
if verbose:
print(f"\nResultado pela Regra de Simpson 1/3 composta: {float(resultado)}")
if grafico:
try:
plotar_funcao_e_aproximacao(func, a, b, m, metodo='Simpson 1/3 composta')
except Exception as e:
if verbose:
print(f"Não foi possível gerar o gráfico: {e}")
if verbose:
try:
x = symbols('x')
func_expr = sympify(func_clean, locals=SYMPY_LOCALS)
deriv4 = diff(func_expr, x, 4)
deriv4_func = lambdify(x, deriv4, modules=["numpy"])
xs = np.linspace(a, b, 1000)
vals = np.abs(deriv4_func(xs))
derivada_max = float(np.nanmax(vals))
erro = erro_truncamento_composta(a, b, m, derivada_max, 'simpson13')
print(f"Erro de truncamento estimado (Simpson 1/3 composta): {erro}")
except Exception as e:
if verbose:
print(f"Não foi possível calcular o erro automaticamente: {e}")
else:
try:
if input("Deseja plotar a função e a aproximação no intervalo [a,b]? (s/n): ").strip().lower() == 's':
try:
plotar_funcao_e_aproximacao(func, a, b, m, metodo='Simpson 1/3 composta')
except Exception as e:
print(f"Não foi possível gerar o gráfico: {e}")
except EOFError:
pass
try:
if input("Deseja estimar o erro de truncamento? (s/n): ").strip().lower() == 's':
try:
x = symbols('x')
func_expr = sympify(func_clean, locals=SYMPY_LOCALS)
deriv4 = diff(func_expr, x, 4)
deriv4_func = lambdify(x, deriv4, modules=["numpy"])
xs = np.linspace(a, b, 1000)
vals = np.abs(deriv4_func(xs))
derivada_max = float(np.nanmax(vals))
erro = erro_truncamento_composta(a, b, m, derivada_max, 'simpson13')
print(f"Erro de truncamento estimado (Simpson 1/3 composta): {erro}")
except Exception as e:
print(f"Não foi possível calcular o erro automaticamente: {e}")
except EOFError:
pass
print()
return float(resultado)
[documentos]
def simpson_3_8_composta(func, a, b, verbose=False, grafico=None):
'''
Regra de Simpson 3/8 composta para integração numérica.
Parameters
----------
func : str
Expressão da função em termos de ``x``.
a, b : float
Limites do intervalo de integração.
verbose : bool, optional
Se True, imprime detalhes e habilita plot/estimativa de erro.
grafico : bool or None, optional
Controla plotagem: se None e verbose for True, habilita-a.
'''
m, h = pedir_m_ou_h(a, b, 'simpson38')
if m is None:
return None
if grafico is None:
grafico = bool(verbose)
# Tentar vetorizar avaliação
x_sym = symbols('x')
func_clean = func.replace('math.', '')
try:
fexpr = sympify(func_clean, locals=SYMPY_LOCALS)
fvec = lambdify(x_sym, fexpr, modules=['numpy'])
xi = np.linspace(a, b, m + 1)
yi = np.asarray(fvec(xi), dtype=float)
except Exception:
xi = np.array([a + i * h for i in range(m + 1)], dtype=float)
yi = []
safe_math = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")}
for xv in xi:
try:
yv = eval(func, {"__builtins__": None}, {"x": xv, "math": math, **safe_math})
except Exception as e:
if verbose:
print(f"Erro na avaliação da função em x={xv}: {e}")
return None
yi.append(yv)
yi = np.asarray(yi, dtype=float)
# composição para Simpson 3/8
soma = 0.0
for i in range(1, m):
if i % 3 == 0:
soma += 2 * yi[i]
else:
soma += 3 * yi[i]
resultado = (3 * h / 8) * (yi[0] + soma + yi[-1])
if verbose:
print(f"\nResultado pela Regra de Simpson 3/8 composta: {float(resultado)}")
print(f"m (subintervalos): {m}, h (passo): {h}")
if grafico:
try:
plotar_funcao_e_aproximacao(func, a, b, m, metodo='Simpson 3/8 composta')
except Exception as e:
if verbose:
print(f"Não foi possível gerar o gráfico: {e}")
if verbose:
try:
x = symbols('x')
func_expr = sympify(func_clean, locals=SYMPY_LOCALS)
deriv4 = diff(func_expr, x, 4)
deriv4_func = lambdify(x, deriv4, modules=["numpy"])
xs = np.linspace(a, b, 1000)
vals = np.abs(deriv4_func(xs))
derivada_max = float(np.nanmax(vals))
erro = erro_truncamento_composta(a, b, m, derivada_max, 'simpson38')
print(f"Erro de truncamento estimado (Simpson 3/8 composta): {erro}")
except Exception as e:
if verbose:
print(f"Não foi possível calcular o erro automaticamente: {e}")
else:
try:
if input("Deseja plotar a função e a aproximação no intervalo [a,b]? (s/n): ").strip().lower() == 's':
try:
plotar_funcao_e_aproximacao(func, a, b, m, metodo='Simpson 3/8 composta')
except Exception as e:
print(f"Não foi possível gerar o gráfico: {e}")
except EOFError:
pass
try:
if input("Deseja estimar o erro de truncamento? (s/n): ").strip().lower() == 's':
try:
x = symbols('x')
func_expr = sympify(func_clean, locals=SYMPY_LOCALS)
deriv4 = diff(func_expr, x, 4)
deriv4_func = lambdify(x, deriv4, modules=["numpy"])
xs = np.linspace(a, b, 1000)
vals = np.abs(deriv4_func(xs))
derivada_max = float(np.nanmax(vals))
erro = erro_truncamento_composta(a, b, m, derivada_max, 'simpson38')
print(f"Erro de truncamento estimado (Simpson 3/8 composta): {erro}")
except Exception as e:
print(f"Não foi possível calcular o erro automaticamente: {e}")
except EOFError:
pass
print()
return float(resultado)
[documentos]
def dados():
"""Menu interativo para métodos de integração numérica."""
print("\n=== Métodos de Integração Numérica ===")
print("1 - Regra do Trapézio")
print("2 - Regra de Simpson 1/3")
print("3 - Regra de Simpson 3/8")
print("0 - Sair")
opcao = input("Escolha o método desejado: ")
return opcao
if __name__ == "__main__":
menu()