CB2325NumericaG5 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,251 @@
1
+ from CB2325NumericaG5.graficos_aproximacao import grafico_ajuste_linear, grafico_ajuste_polinomial
2
+
3
+ def media(dado: list) -> float:
4
+ """Esta função retorna a média dos elementos da lista dada.
5
+
6
+ Args:
7
+ dado (list): Números (inteiros ou float).
8
+
9
+ Raises:
10
+ ValueError: A lista está vazia.
11
+
12
+ Returns:
13
+ float: A média aritmética dos dados fornecidos.
14
+ """
15
+ if len(dado) == 0:
16
+ raise ValueError("A lista está vazia.")
17
+
18
+ soma = sum(dado)
19
+
20
+ return float(soma/(len(dado))) #deixado float explícito aqui - não era necessário, mas fica mais claro
21
+
22
+ def coeficiente_determinacao(valores_y:list, valores_y_ajustados:list) -> float:
23
+ """Calcula o coeficiente de determinação R² para avaliar a qualidade do ajuste.
24
+
25
+ Args:
26
+ valores_y (list): Valores reais de y.
27
+ valores_y_ajustados (list): Valores ajustados de y pela regressão.
28
+
29
+ Raises:
30
+ ValueError: As listas de valores_y e valores_y_ajustados devem ter o mesmo tamanho.
31
+
32
+ Returns:
33
+ float: O coeficiente de determinação R².
34
+ """
35
+ if len(valores_y) != len(valores_y_ajustados):
36
+ raise ValueError("As listas de valores_y e valores_y_ajustados devem ter o mesmo tamanho.")
37
+
38
+ y_medio = media(valores_y)
39
+ soma_erros_quadrados = sum((y - valores_y_ajustados[i]) ** 2 for i, y in enumerate(valores_y))
40
+ soma_total_variacao = sum((y - y_medio) ** 2 for y in valores_y)
41
+
42
+ if soma_total_variacao == 0:
43
+ return 0.0
44
+
45
+ r_quadrado = 1 - (soma_erros_quadrados / soma_total_variacao)
46
+
47
+ return r_quadrado
48
+
49
+ def regressao_linear(valores_x:list, valores_y:list, mostrar_grafico: bool = True, coeficiente_determinacao_r: bool = True) -> tuple :
50
+ """Calcula os coeficientes (angular,linear) da reta que melhor se ajusta aos dados.
51
+ Args:
52
+ valores_x (list): Coordenada x de cada ponto.
53
+ valores_y (list): Coordenada y de cada ponto.
54
+ mostrar_grafico (bool, optional): Indica se o gráfico de dispersão e a reta de ajuste
55
+ devem ser exibidos automaticamente. Por padrão, é True.
56
+ coeficiente_determinacao_r (bool, optional): Indica se o coeficiente de determinação R² deve ser calculado e exibido no terminal. Por padrão, é True.
57
+
58
+ Raises:
59
+ ValueError: A quantidade de abcissas deve ser igual à de ordenadas.
60
+
61
+ Returns:
62
+ tuple: Coeficientes da reta de regressão linear. (Coeficiente angular,Coeficiente linear)
63
+ """
64
+ if len(valores_x) != len(valores_y):
65
+ raise ValueError("A quantidade de abcissas deve ser igual à de ordenadas.")
66
+
67
+ den_beta_chapeu = -len(valores_x)*(media(valores_x)*media(valores_x))
68
+ num_beta_chapeu = -len(valores_x)*media(valores_x)*media(valores_y)
69
+
70
+ for k in range(len(valores_x)):
71
+ den_beta_chapeu += valores_x[k]*valores_x[k]
72
+ num_beta_chapeu += valores_x[k]*valores_y[k]
73
+
74
+ beta_chapeu = num_beta_chapeu/den_beta_chapeu
75
+ alpha_chapeu = media(valores_y) - beta_chapeu*media(valores_x)
76
+ r_quadrado = coeficiente_determinacao(valores_y,[beta_chapeu*x + alpha_chapeu for x in valores_x])
77
+
78
+ if mostrar_grafico == True:
79
+ grafico_ajuste_linear(valores_x,valores_y,beta_chapeu,alpha_chapeu,r_quadrado)
80
+
81
+ if coeficiente_determinacao_r == True:
82
+ print(f"Coeficiente de Determinação R²: {r_quadrado:.2f}")
83
+
84
+ return (beta_chapeu,alpha_chapeu)
85
+
86
+ def resolvedor_de_sistemas(MC:list, VI:list, tolerancia:float = 1e-11) -> list:
87
+ """Resolve Sistemas Lineares. Medios e grandes é por Gauss-Jordan.
88
+
89
+ Args:
90
+ MC (list): Os coeficientes das incognitas do sistema linear.
91
+ VI (list): Os coeficientes independentes das variáveis.
92
+ tolerancia (float, optional): Abaixo desse valor o número é considerado zero; isso para que não haja divisões por números muito pequenos. Defaults to 1e-11.
93
+
94
+ Raises:
95
+ ValueError: Sistema impossível de ser resolvido, alguma linha deu apenas 0.
96
+
97
+ Returns:
98
+ list: Lista do valor de cada incognita, caso venha na ordem MC = [[a_x + a_y + a_z + ...], [b_x + b_y+...], ...], a resposta virá em [x,y,z,...].
99
+ """
100
+ Matriz_Coeficientes = list(MC)
101
+ Vetor_Independentes = list(VI)
102
+ Matriz_Aumentada = [Matriz_Coeficientes[e] + [Vetor_Independentes[e]] for e in range(len(Vetor_Independentes))]
103
+
104
+ def prod_linha(linha:list, produtado:float, div=False) -> list[float]:
105
+
106
+ auxiliar = [r for r in linha]
107
+
108
+ if div == True:
109
+ for r in range(len(linha)):
110
+ auxiliar[r] /= produtado
111
+
112
+ else:
113
+ for r in range(len(linha)):
114
+ auxiliar[r] *= produtado
115
+
116
+ return auxiliar
117
+
118
+ def soma_linha_linha(linha:list, somado:list, sub:bool =False) -> list[float]:
119
+ auxiliar = [r for r in linha]
120
+
121
+ if sub == True:
122
+ for r in range(len(linha)):
123
+ auxiliar[r] -= somado[r]
124
+
125
+ else:
126
+ for r in range(len(linha)):
127
+ auxiliar[r] += somado[r]
128
+
129
+ return auxiliar
130
+
131
+ for kk in range(len(Vetor_Independentes)):
132
+ linhas_trocadas = False
133
+ if abs(Matriz_Aumentada[kk][kk]) <= tolerancia:
134
+ for j in range(kk+1, len(Vetor_Independentes)):
135
+ if abs(Matriz_Aumentada[j][kk]) > tolerancia:
136
+ Matriz_Aumentada[j], Matriz_Aumentada[kk] = Matriz_Aumentada[kk], Matriz_Aumentada[j]
137
+ linhas_trocadas = True
138
+ break
139
+
140
+ if linhas_trocadas == False:
141
+ raise ValueError ("Sistema sem solução única.")
142
+
143
+ e = Matriz_Aumentada[kk][kk]
144
+ transicao = list(Matriz_Aumentada[kk])
145
+ Matriz_Aumentada[kk] = prod_linha(transicao, e, True) #Divide aquela linha pelo elemento da diagonal.
146
+
147
+ for i in range(kk+1, len(Vetor_Independentes)): #Processo de triangulação
148
+ if abs(Matriz_Aumentada[i][kk]) <= tolerancia:
149
+ Matriz_Aumentada[i][kk] = 0
150
+ continue
151
+ variavel_de_suporte1 = Matriz_Aumentada[i][kk]
152
+ variavel_de_suporte2 = prod_linha(Matriz_Aumentada[kk], variavel_de_suporte1)
153
+ Matriz_Aumentada[i] = soma_linha_linha(Matriz_Aumentada[i], variavel_de_suporte2, True)
154
+
155
+ #A partir daqui já temos uma matriz triangular superior.
156
+ x = [0 for i in range(len(Vetor_Independentes))]
157
+
158
+ for i in reversed(range(len(Vetor_Independentes))):
159
+ soma = sum(Matriz_Aumentada[i][j] * x[j] for j in range(i + 1, len(Vetor_Independentes)))
160
+ x[i] = (Matriz_Aumentada[i][-1] - soma) / Matriz_Aumentada[i][i]
161
+
162
+ return x #retorna [x,y,z,...]
163
+
164
+ def aproximacao_polinomial(lista_de_coordenadas:list, grau_do_polinomio:int, mostrar_grafico: bool = True, coeficiente_determinacao_r: bool = True) -> list[float]:
165
+ """Utiliza MMQ para fazer a regressão polinomial dos pontos dados. Tudo no plano. Retorna os coeficientes.
166
+
167
+ Args:
168
+ lista_de_coordenadas (list): Uma lista dos pontos cuja função vai aproximar.
169
+ grau_do_polinomio (int): Qual tipo de polinômio a função retornará. 1 é linear, por exemplo.
170
+ mostrar_grafico (bool, optional): Indica se o gráfico de dispersão e a curva ajustada
171
+ devem ser exibidos automaticamente. Por padrão, é True.
172
+ coeficiente_determinacao_r (bool, optional): Indica se o coeficiente de determinação R² deve ser calculado e exibido no terminal. Por padrão, é True.
173
+
174
+ Raises:
175
+ KeyError: Caso haja menos dados do que o número do grau do polinômio requerido, existirão infinitas "soluções".
176
+
177
+ Returns:
178
+ list: Lista dos coeficientes em ordem crescente de grau.
179
+ """
180
+ quantidade_de_pontos = len(lista_de_coordenadas)
181
+
182
+ if quantidade_de_pontos < grau_do_polinomio+1: #Condição necessária para que um polinômio seja encontrado.
183
+ raise KeyError("A quantidade de dados deve ser maior ou igual ao grau do polinômio desejado.")
184
+ #(
185
+ valores_x = [e for e,ee in lista_de_coordenadas]
186
+ valores_y = [ee for e,ee in lista_de_coordenadas]
187
+ # )Isola cada conjunto de dados de cada coordenada num vetor.
188
+
189
+ matriz_valores_x = [[e**i for e in valores_x] for i in range(grau_do_polinomio+1)]
190
+
191
+ #Feita a matriz de cada xis nos devidos graus para que o polinômio seja encontrado.
192
+ def produto_de_linhas(linha1:list, linha2:list) -> float: #Retorna o elemento da matriz resultado, quando se trata do produto de matrizes.
193
+ """Executa uma operação entre listas do mesmo tamanho.
194
+
195
+ Args:
196
+ linha1 (list): Lista de floats.
197
+ linha2 (list): Lista de floats.
198
+
199
+ Returns:
200
+ int: O resultado do "produto interno" dos "vetores" fornecidos.
201
+ """
202
+ contador = 0
203
+ if len(linha1) == len(linha2):
204
+ for i in range(len(linha1)):
205
+ contador += linha1[i]*linha2[i]
206
+
207
+ return contador
208
+
209
+ matriz_produto_valores_x = [[produto_de_linhas(matriz_valores_x[i],matriz_valores_x[ii]) for i in range(len(matriz_valores_x))] for ii in range(len(matriz_valores_x[0])+1 - len(valores_x)+grau_do_polinomio)] #Menos um ou menos 2? Talvez 1 - (quantidade de dados - grau do polinomio)
210
+ #A matriz que define o sistema de equações que deve ser resolvido. A outra é:
211
+
212
+ vetor_valores_y_do_sistema = [produto_de_linhas(valores_y,matriz_valores_x[i]) for i in range(len(matriz_valores_x))]
213
+ vetor_solucao = resolvedor_de_sistemas(matriz_produto_valores_x,vetor_valores_y_do_sistema)
214
+ valores_y_ajustados = [
215
+ sum(vetor_solucao[i]*(x**i) for i in range(len(vetor_solucao)))
216
+ for x in valores_x
217
+ ]
218
+
219
+ r_quadrado = coeficiente_determinacao(valores_y,valores_y_ajustados)
220
+
221
+ if mostrar_grafico == True:
222
+ grafico_ajuste_polinomial(valores_x, valores_y, vetor_solucao, r_quadrado)
223
+
224
+ if coeficiente_determinacao_r == True:
225
+ print(f"Coeficiente de Determinação R²: {r_quadrado:.2f}")
226
+
227
+ return vetor_solucao
228
+
229
+ def txt_aproximacao_polinomial(lista_de_coordenadas:list, grau_do_polinomio:int) -> str:
230
+ """Utiliza MMQ para fazer a regressão polinomial dos pontos dados. Tudo no plano. Retorna o polinômio.
231
+
232
+ Args:
233
+ lista_de_coordenadas (list): Uma lista dos pontos cuja função vai aproximar.
234
+ grau_do_polinomio (int): Qual tipo de polinômio a função retornará. 1 é linear, por exemplo.
235
+
236
+ Returns:
237
+ str: O polinômio na sua forma por extenso.
238
+ """
239
+ k = str()
240
+ a = aproximacao_polinomial(lista_de_coordenadas,grau_do_polinomio, False, False) #não mostrar o gráfico e o R² duas vezes
241
+
242
+ for i in range(len(a)):
243
+ if a[len(a)-1-i] > 0:
244
+ k+=f"({a[len(a)-1-i]:.3})x^{len(a)-i-1}"
245
+ elif a[len(a)-1-i] == 0:
246
+ continue
247
+ else:
248
+ k+=f" + ({a[len(a)-1-i]:.3})x^{len(a)-i-1} "
249
+
250
+ return k
251
+
@@ -0,0 +1,104 @@
1
+ """
2
+ Módulo para cálculo de erros numéricos.
3
+
4
+ Esse módulo fornece funções para calcular o erro absoluto e o erro relativo entre um valor real e um valor aproximado.
5
+ """
6
+ from typing import List
7
+
8
+
9
+ def erro_absoluto(valor_real: float, valor_aprox: float, casas_decimais: int = 6) -> float:
10
+ """
11
+ Retorna o erro absoluto entre o valor real de um número e seu valor aproximado,
12
+ de acordo com a quantidade desejada de casas decimais.
13
+ Calcula o erro absoluto entre dois valores de um mesmo número: seu valor real e seu valor
14
+ aproximado. O erro absoluto é calculado por meio do módulo da diferença entre o valor real e o
15
+ valor aproximado.
16
+
17
+ Args:
18
+ valor_real (float): É o valor real (ou exato, caso seja) do número
19
+ valor_aprox (float): É o valor aproximado do número
20
+ casas_decimais (int, optional): Número de casas decimais do resultado. Por padrão, é 6.
21
+
22
+ Returns:
23
+ float: Resultado do cálculo do erro absoluto
24
+
25
+ Raises:
26
+ TypeError: se o `valor_real` e o `valor_aprox` não forem float ou int
27
+ ValueError: se `casas_decimais` não for um inteiro não negativo
28
+ """
29
+ if not isinstance(valor_aprox, (int, float)) or not isinstance(valor_real, (int, float)):
30
+ raise TypeError("O valor real e o valor aproximado devem ser números reais.")
31
+ if not isinstance(casas_decimais, int) or casas_decimais<0:
32
+ raise ValueError("O número de casas decimais deve ser um inteiro não-negativo.")
33
+ erro = abs(valor_real - valor_aprox)
34
+ return round(erro, casas_decimais)
35
+
36
+ def erro_relativo(valor_real: float, valor_aprox: float, casas_decimais: int = 6) -> float:
37
+ """
38
+ Retorna o erro relativo entre o valor real de um número e seu valor aproximado, de acordo
39
+ com a quantidade desejada de casas decimais.
40
+ O erro relativo é o erro absoluto dividido pelo valor absoluto do valor real.
41
+ Fórmula: E_r=|(valor_real-valor_aprox)/valor_real|
42
+
43
+ Args:
44
+ valor_real (float): O valor exato ou de referência. Deve ser diferente de zero.
45
+ valor_aprox (float): O valor obtido por medição ou aproximação.
46
+ casas_decimais (int, optional): Número de casas decimais do resultado. Por padrão, é 6.
47
+
48
+ Returns:
49
+ float: O erro relativo calculado, arredondado para o número de casas decimais
50
+ especificado em `casas_decimais`.
51
+
52
+ Raises:
53
+ ValueError: Se o `valor_real` for zero, o que causaria divisão por zero.
54
+ TypeError: se o `valor_real` e o `valor_aprox` não forem float ou int
55
+ ValueError: se `casas_decimais` não for um inteiro não negativo
56
+ """
57
+
58
+ if valor_real==0:
59
+ raise ValueError("O valor real não pode ser zero para o cálculo do erro relativo.")
60
+ if not isinstance(valor_aprox, (int, float)) or not isinstance(valor_real, (int, float)):
61
+ raise TypeError("O valor real e o valor aproximado devem ser números reais.")
62
+ if not isinstance(casas_decimais, int) or casas_decimais<0:
63
+ raise ValueError("O número de casas decimais deve ser um inteiro não-negativo.")
64
+
65
+ return round(abs((valor_real-valor_aprox)/valor_real), casas_decimais)
66
+
67
+ def soma_de_kahan(lista_de_valores: List[float]) -> float:
68
+ """
69
+ O algoritmo de soma de Kahan é um método de análise numérica que
70
+ visa minimizar o erro numérico (erro de arredondamento) ao somar
71
+ uma sequência de números de ponto flutuante de precisão finita.
72
+ Se o usuário decidir somar uma lista de números que possua valores
73
+ muito grandes e valores muito pequenos, como a precisão dos computadores
74
+ é limitida, os dígitos de baixa ordem (menos significativos) do número
75
+ pequeno podem ser perdidos durante a operação de adição devido ao
76
+ arrendondamento. Para minimizar essa falha, o algoritmo da soma de Kahan
77
+ mantém uma variável de "compensação" durante a realização da soma, a qual
78
+ acumula os dígitos de baixa ordem "perdidos" a cada etapa da operação. A
79
+ cada nova etapa de adição, esse erro é usado para "ajustar" o próximo número
80
+ que será somado, compensando a perda de precisão do anterior.
81
+
82
+ Args:
83
+ lista_de_valores (List[float]): A lista dos valores de ponto fluante que se deseja somar
84
+
85
+ Returns:
86
+ float: Retorna a soma compensada dos números de ponto flutuante fornecidos
87
+ """
88
+
89
+ if not isinstance(lista_de_valores, list):
90
+ raise TypeError ("a lista de valores fornecida deve ser uma lista")
91
+
92
+ soma_total = 0.0 # a soma corrente
93
+ compensação = 0.0 # a variável que será a compensação
94
+
95
+ for valor in lista_de_valores:
96
+ if not isinstance(valor, float) and not isinstance(valor, int): # se o valor fornecido não for um float ou um inteiro, a operação não é realizada
97
+ continue
98
+
99
+ v = valor - compensação # a cada valor, desconta-se a compensação
100
+ soma_auxiliar = soma_total + v # a soma auxiliar pega a soma total corrente e adicona o valor após a compensação
101
+ compensação = (soma_auxiliar - soma_total) - v # a compensação é atualizada
102
+ soma_total = soma_auxiliar # a soma total corrente é atualizada com o valor da soma armazenada na variável auxiliar
103
+
104
+ return soma_total
@@ -0,0 +1,60 @@
1
+ import matplotlib.pyplot as plt
2
+ import numpy as np
3
+
4
+ def grafico_ajuste_linear(valores_x:list, valores_y:list, coeficiente_angular:float, coeficiente_linear:float, r_quadrado:float) -> None:
5
+ """Gera o gráfico de dispersão dos pontos e a reta de ajuste linear.
6
+ Args:
7
+ valores_x (list): Coordenada x de cada ponto.
8
+ valores_y (list): Coordenada y de cada ponto.
9
+ coeficiente_angular (float): Coeficiente angular da reta de ajuste linear.
10
+ coeficiente_linear (float): Coeficiente linear da reta de ajuste linear.
11
+ r_quadrado (float): Coeficiente de determinação R².
12
+ """
13
+ plt.scatter(valores_x, valores_y, color='blue', label='Pontos de dados')
14
+
15
+ x_min = min(valores_x)
16
+ x_max = max(valores_x)
17
+ y_min = coeficiente_angular * x_min + coeficiente_linear
18
+ y_max = coeficiente_angular * x_max + coeficiente_linear
19
+
20
+ plt.plot([x_min, x_max], [y_min, y_max], color='red', label=f'Reta de Ajuste Linear (R² = {r_quadrado:.2f})')
21
+
22
+ plt.xlabel('Valores X')
23
+ plt.ylabel('Valores Y')
24
+ plt.title('Ajuste Linear')
25
+ plt.legend()
26
+ plt.grid(True)
27
+ plt.show()
28
+ return None
29
+
30
+
31
+ #versão utilizando numpy - a reta de ajuste fica mais suave
32
+ def grafico_ajuste_polinomial(valores_x:list, valores_y:list, coeficientes:list, r_quadrado:float) -> None:
33
+ """Gera o gráfico de dispersão dos pontos e a curva de ajuste polinomial.
34
+ Args:
35
+ valores_x (list): Coordenada x de cada ponto.
36
+ valores_y (list): Coordenada y de cada ponto.
37
+ coeficientes (list): Coeficientes do polinômio de ajuste.
38
+ r_quadrado (float): Coeficiente de determinação R².
39
+ """
40
+ plt.scatter(valores_x, valores_y, color='blue', label='Pontos de dados')
41
+
42
+ #cria 200 pontos igualmente espaçados entre o min e o max de x para uma curva mais suave
43
+ intervalo_x = np.linspace(min(valores_x), max(valores_x), 200)
44
+
45
+ valores_y_ajustados = []
46
+ #calcula e adiciona os valores de y ajustados usando a expressão do polinomio
47
+ for x in intervalo_x:
48
+ y=0
49
+ for i in range(len(coeficientes)):
50
+ y += coeficientes[i] * (x ** i)
51
+ valores_y_ajustados.append(y)
52
+
53
+ plt.plot(intervalo_x, valores_y_ajustados, color='red', label=f'Curva de Ajuste Polinomial (R² = {r_quadrado:.2f})')
54
+
55
+ plt.xlabel('Valores X')
56
+ plt.ylabel('Valores Y')
57
+ plt.title('Ajuste Polinomial')
58
+ plt.legend()
59
+ plt.grid(True)
60
+ plt.show()
@@ -0,0 +1,190 @@
1
+ """
2
+ Módulo de integração numérica — cálculo e visualização de integrais definidas.
3
+
4
+ Este módulo fornece ferramentas para o cálculo numérico de integrais definidas
5
+ de funções reais em um intervalo [a, b], utilizando dois métodos clássicos:
6
+ - Regra dos Trapézios
7
+ - Regra de Simpson (1/3)
8
+
9
+ Além de realizar o cálculo, gera representações gráficas
10
+ da área sob a curva, facilitando a visualização dos conceitos de integração.
11
+
12
+ Exemplo de uso:
13
+ ---------------
14
+ >>> from CB2325NumericaG5.integracao import integral
15
+ >>> resultado = integral(lambda x: x**2, 0, 2, n=10, metodo="simpson", plot=True)
16
+ >>> print(resultado)
17
+ 2.6667
18
+ """
19
+
20
+ from typing import Callable, Union
21
+ import matplotlib.pyplot as plt
22
+ import numpy as np
23
+
24
+ def _plot_parabola(x0: float, x1: float, x2: float, y0: float, y1: float, y2: float) -> None:
25
+ """
26
+ Gera a representação gráfica de uma parábola que passa por (x0,y0), (x1,y1) e (x2,y2) no intervalo [x0,x2],
27
+ preenchendo a área entre a curva e o eixo x.
28
+
29
+ Esta função não retorna valor, apenas gera uma representação gráfica da parábola.
30
+
31
+ Args:
32
+ x0 (float): x do primeiro ponto da parábola
33
+ x1 (float): x do segundo ponto da parábola
34
+ x2 (float): x do terceiro ponto da parábola
35
+ y0 (float): y do primeiro ponto da parábola
36
+ y1 (float): y do segundo ponto da parábola
37
+ y2 (float): y do terceiro ponto da parábola
38
+ """
39
+ # Encontrando coeficientes a, b, c da parábola y = ax² + bx + c:
40
+ A = np.array([[x0**2, x0, 1],
41
+ [x1**2, x1, 1],
42
+ [x2**2, x2, 1]])
43
+ y_vec = np.array([y0, y1, y2])
44
+ a, b, c = np.linalg.solve(A, y_vec)
45
+
46
+ # Pontos da parábola
47
+ x_para = np.linspace(x0, x2, 50)
48
+ y_para = a*x_para**2 + b*x_para + c
49
+
50
+ plt.plot(x_para, y_para, 'r--', alpha=0.7, linewidth=1)
51
+ plt.fill_between(x_para, 0, y_para, alpha=0.2, color='red')
52
+
53
+ def integral(
54
+ funcao: Callable[[Union[float, np.ndarray]], Union[float, np.ndarray]],
55
+ a: float,
56
+ b: float,
57
+ n: int,
58
+ aprox: int = 4,
59
+ metodo: str = "simpson",
60
+ plot: bool = True
61
+ ) -> float:
62
+ """
63
+ Calcula numericamente a integral definida de uma função no intervalo [a, b],
64
+ utilizando o método dos trapézios ou a regra de Simpson (1/3) e
65
+ gera uma representação gráfica da integral.
66
+
67
+ Se `a > b`, o resultado será negativo, seguindo a convenção matemática.
68
+
69
+ O método dos trapézios aproxima a área sob a curva dividindo o intervalo em
70
+ `n` subintervalos igualmente espaçados e somando as áreas dos trapézios formados
71
+ entre os pontos consecutivos.
72
+
73
+ Já a regra de Simpson aproxima a integral pela soma das áreas sob polinômios quadráticos
74
+ que interpolam a função, proporcionando maior precisão com o mesmo número de
75
+ subdivisões — desde que `n` seja par.
76
+
77
+ Args:
78
+ funcao (Callable[[Union[float, np.ndarray]], Union[float, np.ndarray]]): Função a ser integrada.
79
+ a (float): Limite inferior de integração.
80
+ b (float): Limite superior de integração.
81
+ n (int): Número de subdivisões (intervalos) utilizados na aproximação. Para o método de Simpson, `n` deve ser par.
82
+ aprox (int, optional): Número de casas decimais para arredondamento do resultado. O padrão é 4.
83
+ metodo (str, optional): Método de integração numérica a ser utilizado: `"trapezios"` ou `"simpson"`. O padrão é `"simpson"`.
84
+ plot (bool, optional): Se True, exibe a representação gráfica da integral. O padrão é True.
85
+
86
+ Raises:
87
+ ValueError: Se o método for `"simpson"` e `n` for ímpar.
88
+ ValueError: Se o argumento `metodo` não for `"trapezios"` nem `"simpson"`.
89
+ ValueError: Se `n` não for um inteiro positivo.
90
+ ValueError: Se `aprox` não for um inteiro não negativo.
91
+ TypeError: Se os limites de integração `a` e `b` não forem números reais.
92
+ TypeError: Se `funcao` não for chamável (callable).
93
+
94
+ Returns:
95
+ float: Valor aproximado da integral definida de `funcao` no intervalo [a, b],
96
+ arredondado para o número de casas decimais especificado em `aprox`.
97
+ """
98
+ # Validação da entrada
99
+ if not callable(funcao):
100
+ raise TypeError("O argumento 'funcao' deve ser chamável (callable).")
101
+ if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
102
+ raise TypeError("Os limites de integração 'a' e 'b' devem ser números reais.")
103
+ if not isinstance(n, int) or n <= 0:
104
+ raise ValueError("O número de subdivisões 'n' deve ser um inteiro positivo (> 0).")
105
+ if not isinstance(aprox, int) or aprox < 0:
106
+ raise ValueError("O número de casas decimais 'aprox' deve ser um inteiro não negativo (>= 0).")
107
+ if metodo not in ("trapezios", "simpson"):
108
+ raise ValueError("Método inválido! Use 'trapezios' ou 'simpson'.")
109
+
110
+ funcao_vec = np.vectorize(funcao, otypes=[float])
111
+
112
+ # Cálculo da integral
113
+ dx = (b - a) / n
114
+ soma = 0.0
115
+
116
+ if metodo == "trapezios":
117
+ for i in range(n):
118
+ x1 = a + i * dx
119
+ x2 = a + (i + 1) * dx
120
+ soma += (float(funcao_vec(x1)) + float(funcao_vec(x2))) * dx / 2
121
+
122
+ else: # metodo == "simpson"
123
+ if n % 2 != 0:
124
+ raise ValueError("Para o método de Simpson, o número de subintervalos 'n' deve ser par.")
125
+ for i in range(n + 1):
126
+ x = a + i * dx
127
+ if i == 0 or i == n:
128
+ soma += float(funcao_vec(x))
129
+ elif i % 2 == 1:
130
+ soma += 4 * float(funcao_vec(x))
131
+ else:
132
+ soma += 2 * float(funcao_vec(x))
133
+
134
+ soma *= dx / 3
135
+
136
+ if plot:
137
+ x = np.linspace(a-0.11*(b-a), b + 0.11*(b-a), 100)
138
+ y = funcao_vec(x)
139
+
140
+ plt.figure(figsize=(10, 6))
141
+
142
+ plt.plot(x, y, label='f(x)', linewidth=2, linestyle='-', color='black')
143
+
144
+ plt.title(f"Integração de f(x) = {round(float(soma), aprox)} — método: {metodo.capitalize()}", fontsize=16, fontweight='bold')
145
+ plt.xlabel("x", fontsize=12)
146
+ plt.ylabel("y", fontsize=12)
147
+ plt.grid(True, alpha=0.3)
148
+ plt.legend(fontsize=12)
149
+
150
+ # Garantia do eixo x (y=0) estar sempre no gráfico
151
+ y_min = min(np.min(y), 0)
152
+ y_max = max(np.max(y), 0)
153
+
154
+ # Margem para visualização total do gráfico
155
+ margem_y = 0.1 * (y_max - y_min)
156
+ margem_x = 0.1 * (b - a)
157
+ plt.ylim(y_min - margem_y, y_max + margem_y)
158
+ plt.xlim(a-margem_x, b + margem_x)
159
+
160
+ #Eixos x e y
161
+ plt.axhline(y=0, color='black', linewidth=1.5, linestyle='-')
162
+ plt.axvline(x=0, color='black', linewidth=1.5, linestyle='-')
163
+
164
+ # Limites da integração
165
+ plt.plot([a, a], [0, funcao_vec(a)], color='black', linewidth=1.5, linestyle='-')
166
+ plt.plot([b, b], [0, funcao_vec(b)], color='black', linewidth=1.5, linestyle='-')
167
+
168
+ # Plot da integral
169
+ if metodo == "trapezios":
170
+ for i in range(n):
171
+ x_i = a + i * dx
172
+ x_i2 = a + (i + 1) * dx
173
+
174
+ x_trap = [x_i, x_i, x_i2, x_i2, x_i] # Pontos do trápezio (inf esquerdo, sup esquerdo, sup direito, inf direito)
175
+ y_trap = [0, funcao_vec(x_i), funcao_vec(x_i2), 0, 0] # 5º ponto para fechar o polígono
176
+
177
+ plt.fill(x_trap, y_trap, alpha=0.3, color='orange', edgecolor='black')
178
+
179
+ else: # metodo == "simpson"
180
+ for i in range(0,n,2):
181
+ x_i = a + i * dx
182
+ x_i2 = a + (i + 1) * dx
183
+ x_i3 = a + (i + 2) * dx
184
+ y_i, y_i2, y_i3 = float(funcao_vec(x_i)), float(funcao_vec(x_i2)), float(funcao_vec(x_i3))
185
+
186
+ _plot_parabola(x_i, x_i2, x_i3, y_i, y_i2, y_i3)
187
+
188
+ plt.show()
189
+
190
+ return round(float(soma), aprox)