Código-fonte para codigos.raizes

"""
Módulo para métodos de busca de raízes de equações.

Este módulo implementa algoritmos numéricos para encontrar raízes de
funções: bisseção, Newton-Raphson e secante.

Author: Pedro Henrique Rocha de Andrade
Date: Fevereiro 2026
"""

import matplotlib.pyplot as plt
import numpy as np
import math

# Função para converter a string em função executável (avaliação segura, restrita)
[documentos] def f(x, func_str): """Avalia `func_str` em `x` com ambiente seguro (sem builtins). Parameters ---------- x : float Valor de avaliação. func_str : str Expressão Python da função em x (ex.: 'x**2 - 4'). Returns ------- float Valor de f(x). Raises ------ ValueError Em caso de erro na avaliação. """ # Funções matemáticas permitidas (do módulo math) safe_math = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")} try: return eval(func_str, {"__builtins__": None}, {"x": x, "math": math, **safe_math}) except Exception as e: raise ValueError(f"Erro ao avaliar função em x={x}: {e}")
# Função para plotar gráfico
[documentos] def plotar_funcao(func_str, a=None, b=None, raiz=None, grafico=None, verbose=False, title=None): """Plota a função `f(x)` opcionalmente mostrando a raiz. Parameters ---------- func_str : str Expressão Python de ``f(x)``. a, b : float, optional Intervalo de plotagem (padrão: [-10, 10] se None). raiz : float, optional Ponto da raiz a ser destacado. grafico : bool or None, optional Controla se o gráfico será exibido. Se None, segue ``verbose`` (True mostra o gráfico). verbose : bool, optional Habilita saídas e, por padrão, ativa o gráfico. """ if grafico is None: grafico = bool(verbose) if a is None or b is None: a, b = -10, 10 if not grafico: return x_vals = np.linspace(a, b, 400) # avalia ponto-a-ponto usando nossa função segura y_vals = [] for xv in x_vals: try: y_vals.append(f(xv, func_str)) except Exception: y_vals.append(float('nan')) plt.figure() plt.axhline(0, color="black", linewidth=1) plt.axvline(0, color="black", linewidth=1) # Ajusta legenda para não cortar label_fx = f"f(x) = {func_str}" if len(label_fx) > 40: label_fx = "f(x)" # Plota apenas a função plt.plot(x_vals, y_vals, label=label_fx) # Adiciona tracejado e ponto na raiz, ambos com a mesma label if raiz is not None: try: y_raiz = f(raiz, func_str) plt.axvline(raiz, color="red", linestyle="--", linewidth=1, label=f"Raiz ≈ {str(raiz)}") plt.scatter(raiz, y_raiz, color="red", zorder=5) except Exception: pass plt.xlabel("x") plt.ylabel("f(x)") plt.legend(loc='best') # Título customizável vindo do menu if title is not None: try: plt.title(title) except Exception: pass plt.grid(True) try: plt.show() except Exception: if verbose: print("Aviso: matplotlib não conseguiu exibir o gráfico.")
# Método da Bisseção
[documentos] def bissecao(func_str, a, b, tol, max_iter, verbose=False, grafico=None): """Método da bisseção para encontrar raiz de uma função dada por string. Parameters ---------- func_str : str Expressão Python de ``f(x)`` (ex.: ``'x**2 - 4'``). a, b : float Intervalo inicial [a, b] com sinais opostos. tol : float Tolerância para critério de parada. max_iter : int Número máximo de iterações. verbose : bool, optional Se ``True``, imprime detalhes de cada iteração e ativa o gráfico por padrão. grafico : bool or None, optional Controla plotagem: se ``None`` e ``verbose`` for True, o gráfico é mostrado. Returns ------- (float, int) Tupla (raiz_aproximada, n_iter) ou (None, 0) se os sinais em a/b não forem opostos. """ # Validação básica de tipos/valores try: a = float(a); b = float(b); tol = float(tol); max_iter = int(max_iter) except Exception: raise TypeError("a, b, tol devem ser numéricos e max_iter deve ser inteiro") if verbose: print(f"\nCritério de parada: b-a < {tol}") if grafico is None: grafico = bool(verbose) try: fa = f(a, func_str) fb = f(b, func_str) except Exception as e: print(f"Erro na avaliação nos extremos: {e}") return None, 0 if fa * fb >= 0: print("Erro: f(a) e f(b) devem ter sinais opostos.") return None, 0 iteracoes = [] c_ant = None a_atual, b_atual = a, b for i in range(max_iter): intervalo = abs(b_atual - a_atual) # calcula o ponto médio e seu f antes de verificar critério c = (a_atual + b_atual) / 2 try: fc = f(c, func_str) except Exception as e: print(f"Erro ao avaliar f(c): {e}") return None, i iteracoes.append((i, c, fc, intervalo)) if verbose: print(f"[Bisseção] Iter {i}: x = {str(c)}, f(x) = {str(fc)}, b-a = {str(intervalo)}") # Se f(c) for praticamente zero, encontramos a raiz exata try: is_zero = abs(float(fc)) < 1e-12 except Exception: is_zero = False if is_zero: if verbose: print("\nTabela de iterações (Bisseção):") print(f"{'i':>3} {'xi':>24} {'f(xi)':>24} {'b-a':>24}") for it in iteracoes: print(f"{it[0]:3d} {str(it[1]):>24} {str(it[2]):>24} {str(it[3]):>24}") print(f"\nRaiz aproximada: {str(c)}") print(f"Iterações realizadas: {i+1}") return c, i+1 # Critério: apenas o tamanho do intervalo if intervalo < tol: if verbose: print("\nTabela de iterações (Bisseção):") print(f"{'i':>3} {'xi':>24} {'f(xi)':>24} {'b-a':>24}") for it in iteracoes: print(f"{it[0]:3d} {str(it[1]):>24} {str(it[2]):>24} {str(it[3]):>24}") print(f"\nRaiz aproximada: {c}") print(f"Iterações realizadas: {i+1}") return c, i+1 # atualiza intervalo para próxima iteração if fa * fc < 0: b_atual = c fb = fc else: a_atual = c fa = fc c_ant = c if verbose: print("\nTabela de iterações (Bisseção):") print(f"{'i':>3} {'xi':>12} {'f(xi)':>12} {'b-a':>12}") for it in iteracoes: intervalo_str = f"{it[3]:.8f}" print(f"{it[0]:3d} {it[1]:12.6f} {it[2]:12.6f} {intervalo_str:>12}") print(f"\nRaiz aproximada: {c}") print(f"Iterações realizadas: {max_iter}") return c, max_iter
# Método de Newton-Raphson
[documentos] def newton(func_str, x0, tol, max_iter, verbose=False, a=None, b=None): """Método de Newton-Raphson para encontrar raiz de função dada por string. Parameters ---------- func_str : str Expressão Python de ``f(x)``. x0 : float Chute inicial. tol : float Tolerância para critério de parada. max_iter : int Número máximo de iterações. verbose : bool, optional Se True imprime detalhes de cada iteração. Returns ------- (float, int) Tupla (raiz_aproximada, n_iter) ou (None, n_iter) se houver erro. """ try: x0 = float(x0); tol = float(tol); max_iter = int(max_iter) except Exception: raise TypeError("x0 and tol must be numeric, max_iter integer") if verbose: print(f"\nCritério de parada: |xi-xi-1| < {tol}") h = 1e-6 iteracoes = [] x_ant = x0 for i in range(1, max_iter + 1): try: fx = f(x0, func_str) dfx = (f(x0 + h, func_str) - f(x0 - h, func_str)) / (2 * h) except Exception as e: print(f"Erro ao avaliar função/derivada: {e}") return None, i if dfx == 0: print("Erro: derivada zero.") return None, i x1 = x0 - fx / dfx delta = abs(x1 - x0) iteracoes.append((i-1, x0, fx, delta)) if verbose: try: print(f"[Newton] Iter {i-1}: x = {str(x0)}, f(x) = {str(fx)}, |xi-xi-1| = {str(delta)}") except Exception: print(f"[Newton] Iter {i-1}: x = {str(x0)}, f(x) = <erro na avaliação>") if delta < tol: if verbose: print("\nTabela de iterações (Newton-Raphson):") print(f"{'i':>3} {'xi':>24} {'f(xi)':>24} {'|xi-xi-1|':>24}") for it in iteracoes: print(f"{it[0]:3d} {str(it[1]):>24} {str(it[2]):>24} {str(it[3]):>24}") print(f"\nRaiz aproximada: {str(x1)}") print(f"Iterações realizadas: {i}") return x1, i x0 = x1 if verbose: print("\nTabela de iterações (Newton-Raphson):") print(f"{'i':>3} {'xi':>24} {'f(xi)':>24} {'|xi-xi-1|':>24}") for it in iteracoes: print(f"{it[0]:3d} {str(it[1]):>24} {str(it[2]):>24} {str(it[3]):>24}") print(f"\nRaiz aproximada: {str(x0)}") print(f"Iterações realizadas: {max_iter}") return x0, max_iter
# Método da Secante
[documentos] def secante(func_str, x0, x1, tol, max_iter, verbose=False, a=None, b=None): """Método da secante para encontrar raiz de função dada por string. Parameters ---------- func_str : str Expressão Python de ``f(x)``. x0, x1 : float Dois chutes iniciais. tol : float Tolerância para critério de parada. max_iter : int Número máximo de iterações. verbose : bool, optional Se True imprime detalhes de cada iteração. Returns ------- (float, int) Tupla (raiz_aproximada, n_iter) ou (None, n_iter) em caso de erro. """ try: x0 = float(x0); x1 = float(x1); tol = float(tol); max_iter = int(max_iter) except Exception: raise TypeError("x0, x1, tol devem ser numéricos e max_iter deve ser inteiro") if verbose: print(f"\nCritério de parada: |xi-xi-1| < {tol}") iteracoes = [] fx0 = f(x0, func_str) fx1 = f(x1, func_str) iteracoes.append((0, x0, fx0, float('nan'))) iteracoes.append((1, x1, fx1, abs(x1 - x0))) # Começa as iterações a partir do terceiro valor (índice 2) for iter_count in range(2, max_iter + 1): # atualiza checagem de divisão por zero if fx1 - fx0 == 0: print("Erro: divisão por zero.") return None, iter_count - 1 x2 = (x0 * fx1 - x1 * fx0) / (fx1 - fx0) fx2 = f(x2, func_str) delta = abs(x2 - x1) iteracoes.append((iter_count, x2, fx2, delta)) if verbose: try: print(f"[Secante] Iter {iter_count-2}: x = {x2}, f(x) = {fx2}, |xi-xi-1| = {delta}") except Exception: print(f"[Secante] Iter {iter_count-2}: x = {x2}, f(x) = <erro na avaliação>") if delta < tol: if verbose: print("\nTabela de iterações (Secante):") print(f"{'i':>3} {'xi':>24} {'f(xi)':>24} {'|xi-xi-1|':>24}") for it in iteracoes: delta_str = str(it[3]) if not np.isnan(it[3]) else "-" print(f"{it[0]:3d} {str(it[1]):>24} {str(it[2]):>24} {delta_str:>24}") print(f"\nRaiz aproximada: {x2}") print(f"Iterações realizadas: {iter_count-1}") return x2, iter_count-1 # atualiza pontos e valores de f para próxima iteração x0, fx0 = x1, fx1 x1, fx1 = x2, fx2 if verbose: print("\nTabela de iterações (Secante):") print(f"{'i':>3} {'xi':>24} {'f(xi)':>24} {'|xi-xi-1|':>24}") for it in iteracoes: delta_str = str(it[3]) if not np.isnan(it[3]) else "-" print(f"{it[0]:3d} {str(it[1]):>24} {str(it[2]):>24} {delta_str:>24}") print(f"\nRaiz aproximada: {x1}") print(f"Iterações realizadas: {max_iter}") return x1, max_iter
# Menu principal
[documentos] def pedir_dados_raizes(metodo=None): """Lê os dados necessários para o método de raízes. Se ``metodo`` for ``None``, comporta-se como modo interativo completo (executa o método e plota o resultado). Se ``metodo`` for uma das strings ``'bissecao'``, ``'newton'`` ou ``'secante'``, apenas lê os parâmetros e retorna uma tupla ``(func_str, tol, max_iter, params)`` para uso por menus externos (compatibilidade com `menu_raizes`). Em modo interativo, protege contra EOFError para permitir execuções em ambientes não interativos (scripts/tests). """ if metodo is None: # modo interativo (executa e plota) try: print("Métodos de Cálculo Numérico") print("1 - Método da Bisseção") print("2 - Método de Newton-Raphson (Tangente)") print("3 - Método da Secante") escolha = int(input("Escolha o método: ")) func_str = input("Digite a função f(x) (ex: x**2 - 4): ") # Verificação para evitar '=' na expressão if '=' in func_str: print("Erro: Digite apenas a expressão igualada a zero, por exemplo: x**4 - 2*x**3 - 2*x + 1") return None tol_str = input("Digite a tolerância: ") try: tol = float(tol_str) except Exception: print("Erro: Tolerância deve ser um número (ex: 0.0001 ou 1e-4)") return None max_iter_str = input("Digite o número máximo de iterações [padrão: 100]: ") if max_iter_str.strip() == "": max_iter = 100 else: try: max_iter = int(max_iter_str) except Exception: print("Erro: Número máximo de iterações deve ser um inteiro.") return None if escolha == 1: a = float(input("Digite o valor de a: ")) b = float(input("Digite o valor de b: ")) raiz, iters = bissecao(func_str, a, b, tol, max_iter, verbose=True) print("\nGráfico da função:") plotar_funcao(func_str, a, b, raiz, grafico=True, title="Método da Bisseção") elif escolha == 2: x0 = float(input("Digite o valor inicial x0: ")) a = float(input("Digite o valor de a: ")) b = float(input("Digite o valor de b: ")) raiz, iters = newton(func_str, x0, tol, max_iter, verbose=True, a=a, b=b) print("\nGráfico da função:") plotar_funcao(func_str, a, b, raiz, grafico=True, title="Método de Newton-Raphson") if escolha == 3: x0 = float(input("Digite o valor inicial x0: ")) x1 = float(input("Digite o valor inicial x1: ")) # Define intervalo automaticamente para o gráfico a = min(x0, x1) - 2 b = max(x0, x1) + 2 raiz, iters = secante(func_str, x0, x1, tol, max_iter, verbose=True, a=a, b=b) print("\nGráfico da função:") plotar_funcao(func_str, a, b, raiz, grafico=True, title="Método da Secante") else: print("Opção inválida.") return return except EOFError: # Em execução não interativa, retorna sem exceção return None except Exception as e: print(f"Erro ao ler parâmetros: {e}") return None # Modo de leitura para menu externo: não executar, apenas retornar parâmetros try: func_str = input("Digite a função f(x) (ex: x**2 - 4): ") if '=' in func_str: print("Erro: Digite apenas a expressão igualada a zero, por exemplo: x**4 - 2*x**3 - 2*x + 1") return None, None, None, () tol_str = input("Digite a tolerância: ") try: tol = float(tol_str) except Exception: print("Erro: Tolerância deve ser um número (ex: 0.0001 ou 1e-4)") return None, None, None, () max_iter_str = input("Digite o número máximo de iterações [padrão: 100]: ") if max_iter_str.strip() == "": max_iter = 100 else: try: max_iter = int(max_iter_str) except Exception: print("Erro: Número máximo de iterações deve ser um inteiro.") return None, None, None, () if metodo == 'bissecao': a = float(input("Digite o valor de a: ")) b = float(input("Digite o valor de b: ")) params = (a, b) elif metodo == 'newton': x0 = float(input("Digite o valor inicial x0: ")) params = (x0,) elif metodo == 'secante': x0 = float(input("Digite o valor inicial x0: ")) x1 = float(input("Digite o valor inicial x1: ")) params = (x0, x1) else: print("Método desconhecido para leitura de parâmetros.") return None, None, None, () return func_str, tol, max_iter, params except EOFError: return None, None, None, () except Exception as e: print(f"Erro ao ler parâmetros: {e}") return None, None, None, ()
[documentos] def dados(): """Menu interativo para métodos de busca de raízes.""" print("\n=== Métodos de Busca de Raízes ===") print("1 - Método da Bissecção") print("2 - Método do Ponto Fixo") print("3 - Método de Newton-Raphson") print("4 - Método da Secante") print("0 - Sair") opcao = input("Escolha o método desejado: ") return opcao
if __name__ == "__main__": pedir_dados_raizes()