Código-fonte para CB2325NumericaG1.raizes.raizes_secante

import numpy as np
import matplotlib.pyplot as plt
from typing import Callable

[documentos] def secante(function: Callable, guess0: float, guess1: float, tolerance: float, plot: bool = False) -> float: """ Encontra/aproxima uma raiz de uma função real de variável real usando o método da secante. Calcula onde a reta secante ao gráfico da função nos pontos guess0 e guess1 cruza o eixo x. Repete o processo com esse novo ponto guess2 e o ponto guess1. Args: function (Callable): Função cuja raíz queremos encontrar ou aproximar. guess0 (float): Primeiro chute inicial. guess1 (float): Segundo chute inicial. tolerance (float): Critério de parada. O método para quando |f| < tolerance ou |dx| < tolerance. plot (bool = False): Determina se uma visualização gráfica do método será plotada. Por padrão, não será. Returns: float: Valor aproximado da raiz. Raises: ValueError: Se ocorrer NaN/Inf em algum momento da iteração. Se em algum momento da iteração o ponto xn nao estiver no domínio TypeError: Se function não for Callable. ZeroDivisionError: Se a derivada praticamente zerar em algum momento da iteração. RunTimeError: Se o método não convergir em no máximo 1000 iterações. """ if not callable(function): raise TypeError("function deve ser um Callable.") f = function x_prev = float(guess0) x_curr = float(guess1) # Avalia f no primeiro chute com proteção try: f_prev_raw = f(x_prev) f_prev = float(f_prev_raw) except Exception as e: raise ValueError(f"f(x) não pôde ser avaliada no chute inicial x0 = {x_prev}: {e}") if np.isnan(f_prev) or np.isinf(f_prev): raise ValueError(f"f(x) retornou {f_prev} no chute inicial x0 = {x_prev}.") x_record = [x_prev, x_curr] MAX_ITERS = 1000 root = None f_curr = None for i in range(MAX_ITERS): # Avalia f no segundo chute com proteção try: f_curr_raw = f(x_curr) f_curr = float(f_curr_raw) except Exception as e: raise ValueError(f"f(x) não pôde ser avaliada no ponto x = {x_curr} na iteração {i}: {e}") if np.isnan(f_curr) or np.isinf(f_curr): raise ValueError(f"f(x) retornou {f_curr} no ponto x = {x_curr} na iteração {i}.") # Critério de parada if abs(f_curr) < tolerance: root = x_curr break if abs(f_curr - f_prev) < 1e-16: raise ZeroDivisionError(f"Denominador muito próximo de zero na iteração {i}.") # A fórmula de iteração da Secante x_next = x_curr - f_curr * (x_curr - x_prev) / (f_curr - f_prev) x_record.append(x_next) # Critério de parada if abs(x_next - x_curr) < tolerance: root = x_next break # Atualiza os pontos para a próxima iteração x_prev = x_curr x_curr = x_next f_prev = f_curr if root is None: try: f_last = float(f(x_curr)) except Exception: f_last = float("nan") raise RuntimeError(f"Não convergiu após {MAX_ITERS} iterações. Último x = {x_curr}, f(x) = {f_last}") # Visualização Gráfica if plot: if len(x_record) > 1: x_min = min(x_record) x_max = max(x_record) delta = (x_max - x_min) * 0.1 if x_max > x_min else 1.0 x_min -= delta x_max += delta else: x_min = x_record[0] - 1.0 x_max = x_record[0] + 1.0 # Cria um range denso para plotar a curva da função x_space = np.linspace(x_min, x_max, 400) y_space = f(x_space) # Plota a função original plt.plot(x_space, y_space, color='black', linewidth=1.2, label='f(x)') # Pontos de iteração x_points = np.array(x_record) y_points = f(x_points) plt.plot(x_points, y_points, 'ro', label='Pontos de iteração', zorder=3) # Desenha as secantes for j in range(len(x_record) - 1): x0, y0 = x_record[j], f(x_record[j]) x1, y1 = x_record[j + 1], f(x_record[j + 1]) # Coeficiente angular e intercepto da secante m = (y1 - y0) / (x1 - x0) b = y1 - m * x1 # Extensão da linha (até cruzar o eixo x) x_line = np.linspace(min(x0, x1), max(x0, x1), 10) y_line = m * x_line + b plt.plot(x_line, y_line, 'b--', linewidth=0.8, label='Secante' if j == 0 else None) # Plota o eixo x. plt.axhline(0, color='gray', linewidth=1) # Ajustes visuais plt.grid(True, linestyle='--', alpha=0.6) plt.title("Raízes da função pelo Método da Secante") plt.xlabel("x") plt.ylabel("f(x)") plt.legend() plt.show() return root