Código-fonte para codigos.sistemaslineares

import numpy as np

"""Utilitários para resolução de sistemas lineares.

Módulo contendo funções para montagem e exibição de sistemas lineares,
eliminação de Gauss (com e sem pivotamento), decomposição LU (com e sem
pivotamento), forward/backward solves e cálculo do resíduo.

Todas as funções aceitam entradas ``array_like`` (listas, arrays numpy, etc.),
convertem internamente para ``numpy.ndarray`` quando necessário, e retornam
estruturas numpy compatíveis para fácil integração com demais rotinas.
"""

def _validate_linear_system_inputs(A, b):
    """Valida e converte entradas para funções de sistemas lineares.
    
    Parameters
    ----------
    A : array_like
        Matriz dos coeficientes.
    b : array_like
        Vetor dos termos independentes.
        
    Returns
    -------
    A_valid, b_valid : np.ndarray
        Entradas validadas e convertidas para float.
        
    Raises
    ------
    ValueError
        Se as entradas não forem válidas.
    """
    A = np.asarray(A, dtype=float)
    b = np.asarray(b, dtype=float)
    if A.ndim != 2 or A.shape[0] != A.shape[1]:
        raise ValueError("A deve ser uma matriz quadrada 2D.")
    if b.ndim != 1 or b.shape[0] != A.shape[0]:
        raise ValueError("b deve ser um vetor 1D com comprimento igual ao número de linhas de A.")
    return A, b

def _validate_lu_inputs(A, b):
    """Valida entradas para funções de decomposição LU.
    
    Parameters
    ----------
    A : array_like
        Matriz a ser decomposta.
    b : array_like or None
        Vetor opcional para exibição.
        
    Returns
    -------
    A_valid, b_valid : np.ndarray
        Entradas validadas.
    """
    A = np.asarray(A, dtype=float)
    if A.ndim != 2 or A.shape[0] != A.shape[1]:
        raise ValueError("A deve ser uma matriz quadrada 2D.")
    if b is not None:
        b = np.asarray(b, dtype=float)
        if b.ndim != 1 or b.shape[0] != A.shape[0]:
            raise ValueError("b deve ser um vetor 1D com comprimento igual ao número de linhas de A.")
    return A, b

def imprimir_sistema_linear(A, b, titulo="Sistema de Equações", verbose=True):
    """Exibe um sistema linear A|b formatado no terminal.

    Parameters
    ----------
    A : np.ndarray
        Matriz de coeficientes (n x n).
    b : np.ndarray or None
        Vetor dos termos independentes (n,), ou None se não houver.
    titulo : str, optional
        Título a ser exibido antes do sistema (default: ``'Sistema de Equações'``).
    verbose : bool, optional
        Se True, imprime o sistema; se False, não imprime nada (default: True).
    """
    if not verbose:
        return
    n = len(A)
    largura = 24 
    print(f"\n{titulo}:")
    for i in range(n):
        linha = ""
        for j in range(A.shape[1]):
            linha += f"{A[i,j]:>{largura}} "
        if b is not None:
            linha += f"|{b[i]:>{largura}}"
        print(linha)
    print()
    
[documentos] def montar_sistema_valores(): """Lê interativamente os coeficientes de um sistema linear quadrado. Solicita ao usuário o número de variáveis ``n``, em seguida lê ``n`` linhas com ``n`` coeficientes cada e um vetor ``b`` com ``n`` termos. Faz validações simples de comprimento e converte para ``numpy.ndarray``. Returns ------- A : np.ndarray Matriz dos coeficientes (n x n). b : np.ndarray Vetor dos termos independentes (n,). vars : list Lista de nomes das variáveis (ex.: ['x1','x2', ...]). Notes ----- - Em ambientes não interativos (por exemplo, testes), se houver um EOF na leitura, a função retorna ``None`` para indicar que a leitura não foi completada, evitando a interrupção do programa. Raises ------ ValueError Se o número de elementos fornecidos por linha não corresponder a ``n``. """ try: n = int(input("Digite o número de variáveis (sistema quadrado): ")) print(f"Insira os coeficientes da matriz A ({n} linhas), cada linha com {n} valores separados por espaço:") A = [] for i in range(n): linha = list(map(float, input(f"Linha {i+1}: ").split())) if len(linha) != n: raise ValueError("Número de coeficientes deve ser igual ao número de variáveis!") A.append(linha) print(f"Insira os termos independentes do vetor b em uma única linha, {n} valores separados por espaço:") b = list(map(float, input("b: ").split())) if len(b) != n: raise ValueError("Número de termos independentes deve ser igual ao número de variáveis!") return np.array(A, dtype=float), np.array(b, dtype=float), [f"x{i+1}" for i in range(n)] except EOFError: # Em ambiente não interativo (tests/CI), simplesmente indique que a # leitura não foi realizada, sem levantar exceção. return None
[documentos] def eliminacao_gauss_sem_pivotamento(A, b, verbose=False): """Executa a eliminação de Gauss sem pivotamento parcial. Triangulariza a matriz ``A`` e aplica substituição regressiva para obter a solução ``x``. Esta versão não realiza trocas de linha, por isso pode falhar para matrizes que exigem pivotamento. Parameters ---------- A : array_like, shape (n, n) Matriz dos coeficientes do sistema linear. b : array_like, shape (n,) Vetor dos termos independentes. verbose : bool, optional Se True, imprime os passos da eliminação; se False, executa silenciosamente (default: False). Returns ------- tuple ``(x, A_triangular, b_modificado, erro_flag)`` onde ``x`` é o vetor solução (ou ``None`` em caso de falha), ``A_triangular`` é a matriz A após eliminação e ``b_modificado`` o vetor b modificado. ``erro_flag`` é ``False`` em execução bem-sucedida ou ``True`` em caso de erro. """ A, b = _validate_linear_system_inputs(A, b) n = A.shape[0] A = A.copy() b = b.copy() imprimir_sistema_linear(A, b, "Sistema inicial", verbose) for k in range(n-1): if abs(A[k,k]) < 1e-20: if verbose: print(f"Pivô zero detectado na linha {k+1} no método sem pivotamento. Não é possível continuar.") return None, None, None, True for i in range(k+1, n): fator = -A[i,k] / A[k,k] A[i,k:] += fator * A[k,k:] b[i] += fator * b[k] if verbose: print(f"Eliminando elemento A[{i+1},{k+1}], multiplicando linha {k+1} por {fator} e subtraindo da linha {i+1}:") imprimir_sistema_linear(A, b, f"Sistema após eliminar entrada A[{i+1},{k+1}]", verbose) if any(abs(A[i,i]) < 1e-20 for i in range(n)): if verbose: print("Sistema impossível (pivô zero na diagonal após eliminação).") return None, None, None, True x = np.zeros(n) for i in range(n-1, -1, -1): soma = np.dot(A[i,i+1:], x[i+1:]) x[i] = (b[i] - soma) / A[i,i] return x, A, b, False
[documentos] def eliminacao_gauss_com_pivotamento(A, b, verbose=False): """Executa a eliminação de Gauss com pivotamento parcial. Triangulariza a matriz ``A`` com trocas de linha para escolher o melhor pivô, e aplica substituição regressiva para obter a solução ``x``. Parameters ---------- A : array_like, shape (n, n) Matriz dos coeficientes do sistema linear. b : array_like, shape (n,) Vetor dos termos independentes. verbose : bool, optional Se True, imprime os passos da eliminação; se False, executa silenciosamente (default: False). Returns ------- tuple ``(x, A_triangular, b_modificado, erro_flag)`` onde ``x`` é o vetor solução (ou ``None`` em caso de falha), ``A_triangular`` é a matriz A após eliminação e ``b_modificado`` o vetor b modificado. ``erro_flag`` é ``False`` em execução bem-sucedida ou ``True`` em caso de erro. """ A, b = _validate_linear_system_inputs(A, b) n = A.shape[0] A = A.copy() b = b.copy() imprimir_sistema_linear(A, b, "Sistema inicial", verbose) for k in range(n): pivo_linha = max(range(k, n), key=lambda i: abs(A[i,k])) if verbose: print(f"Pivô escolhido: {pivo_linha}") if abs(A[pivo_linha,k]) < 1e-20: if verbose: print("Sistema impossível (pivô zero detectado).") return None, None, None, True if pivo_linha != k: A[[k,pivo_linha]] = A[[pivo_linha,k]] b[[k,pivo_linha]] = b[[pivo_linha,k]] if verbose: print(f"Trocando linha {k+1} com linha {pivo_linha+1} (pivoteamento):") imprimir_sistema_linear(A, b, f"Sistema após troca das linhas {k+1} e {pivo_linha+1}", verbose) for i in range(k+1, n): fator = -A[i,k] / A[k,k] A[i,k:] += fator * A[k,k:] b[i] += fator * b[k] if verbose: print(f"Eliminando elemento A[{i+1},{k+1}], multiplicando linha {k+1} por {fator} e subtraindo da linha {i+1}:") imprimir_sistema_linear(A, b, f"Sistema após eliminar entrada A[{i+1},{k+1}]", verbose) x = np.zeros(n) for i in range(n-1, -1, -1): soma = np.dot(A[i,i+1:], x[i+1:]) x[i] = (b[i] - soma) / A[i,i] return x, A, b, False
[documentos] def lu_sem_pivot(A, b, verbose=False): """Decomposição LU sem pivotamento. Calcula matrizes ``L`` (inferior) e ``U`` (superior) tais que ``A = L @ U`` quando possível. Não realiza pivotamento; um pivô zero provoca exceção ``ZeroDivisionError``. Parameters ---------- A : array_like, shape (n, n) Matriz a ser decomposta. b : array_like (opcional) vetor usado apenas para exibição durante passos (pode ser ``None`` quando não for necessário). verbose : bool, optional Se True, imprime os passos da decomposição; se False, executa silenciosamente (default: False). Returns ------- L, U : np.ndarray Matrizes inferior e superior da decomposição LU. """ A, b = _validate_lu_inputs(A, b) n = A.shape[0] U = A.astype(float).copy() L = np.eye(n) if verbose: imprimir_sistema_linear(U, b, "Matriz U inicial", verbose) imprimir_sistema_linear(L, b, "Matriz L inicial", verbose) for k in range(n-1): if abs(U[k,k]) < 1e-20: if verbose: print(f"Pivô zero detectado na etapa {k+1} da decomposição LU sem pivotamento.") raise ZeroDivisionError("Pivô zero na LU sem pivotamento - sistema pode ser impossível ou indeterminado.") if verbose: print(f'Etapa {k+1} da decomposição LU sem pivotamento:') for i in range(k+1, n): m = - U[i,k] / U[k,k] L[i,k] = - m U[i,:] += m * U[k,:] if verbose: print(f"m_{{{i+1},{k+1}}} = {m}") imprimir_sistema_linear(L, b, "Matriz L após etapa", verbose) imprimir_sistema_linear(U, b, "Matriz U após etapa", verbose) return L, U
[documentos] def lu_com_pivot(A, b, verbose=False): """Decomposição LU com pivotamento parcial. Retorna a tripla ``P, L, U`` tal que ``P @ A = L @ U``. Parameters ---------- A : array_like, shape (n, n) Matriz a ser decomposta. b : array_like (opcional) vetor usado apenas para exibição durante passos. verbose : bool, optional Se True, imprime os passos da decomposição; se False, executa silenciosamente (default: False). Returns ------- P, L, U : np.ndarray ``P`` matriz de permutação, ``L`` inferior, ``U`` superior. """ A, b = _validate_lu_inputs(A, b) n = A.shape[0] U = A.astype(float).copy() L = np.eye(n) P = np.eye(n) if verbose: imprimir_sistema_linear(U, b, "Matriz U inicial", verbose) imprimir_sistema_linear(L, b, "Matriz L inicial", verbose) imprimir_sistema_linear(P, np.zeros(n), "Matriz P inicial", verbose) for k in range(n-1): pivo_linha = max(range(k, n), key=lambda i: abs(U[i,k])) if abs(U[pivo_linha,k]) < 1e-20: if verbose: print("Sistema impossível (pivô zero detectado na LU com pivotamento).") return None if pivo_linha != k: U[[k, pivo_linha], :] = U[[pivo_linha, k], :] P[[k, pivo_linha], :] = P[[pivo_linha, k], :] if k > 0: L[[k, pivo_linha], :k] = L[[pivo_linha, k], :k] if verbose: print(f"Trocando linha {k+1} com linha {pivo_linha+1} na matriz U (pivotamento):") imprimir_sistema_linear(U, b, "Matriz U após permutação", verbose) imprimir_sistema_linear(L, b, "Matriz L após permutação", verbose) imprimir_sistema_linear(P, np.zeros(n), "Matriz P após permutação", verbose) if verbose: print(f'Etapa {k+1} da decomposição LU com pivotamento:') for i in range(k+1, n): m = -U[i,k] / U[k,k] L[i,k] = -m U[i,:] += m * U[k,:] if verbose: print(f"m_{{{i+1},{k+1}}} = {m}") imprimir_sistema_linear(L, np.zeros(n), "Matriz L após etapa", verbose) imprimir_sistema_linear(U, np.zeros(n), "Matriz U após etapa", verbose) return P, L, U
[documentos] def forward_solve(L, b): """Resolve o sistema triangular inferior L y = b por substituição progressiva. Parameters ---------- L : array_like, shape (n, n) Matriz triangular inferior (diagonal não-nula esperada). b : array_like, shape (n,) Vetor de termos independentes. Returns ------- y : np.ndarray Solução do sistema triangular inferior. """ L = np.asarray(L, dtype=float) b = np.asarray(b, dtype=float) if L.ndim != 2 or L.shape[0] != L.shape[1]: raise ValueError("L deve ser uma matriz quadrada 2D.") if b.ndim != 1 or b.shape[0] != L.shape[0]: raise ValueError("b deve ser um vetor 1D com comprimento igual ao número de linhas de L.") n = L.shape[0] y = np.zeros(n) for i in range(n): y[i] = (b[i] - np.dot(L[i,:i], y[:i])) / L[i,i] return y
[documentos] def backward_solve(U, y): """Resolve o sistema triangular superior U x = y por substituição regressiva. Parameters ---------- U : array_like, shape (n, n) Matriz triangular superior. y : array_like, shape (n,) Vetor do lado direito (resultado da forward_solve). Returns ------- x : np.ndarray Solução do sistema triangular superior. """ U = np.asarray(U, dtype=float) y = np.asarray(y, dtype=float) if U.ndim != 2 or U.shape[0] != U.shape[1]: raise ValueError("U deve ser uma matriz quadrada 2D.") if y.ndim != 1 or y.shape[0] != U.shape[0]: raise ValueError("y deve ser um vetor 1D com comprimento igual ao número de linhas de U.") n = U.shape[0] x = np.zeros(n) for i in range(n-1, -1, -1): x[i] = (y[i] - np.dot(U[i,i+1:], x[i+1:])) / U[i,i] return x
[documentos] def calcular_residuo(A, x, b): """Calcula o resíduo r = b - A x. Parameters ---------- A : array_like Matriz dos coeficientes. x : array_like Solução candidata do sistema. b : array_like Vetor do lado direito. Returns ------- r : np.ndarray Vetor resíduo (b - A x). """ A = np.asarray(A, dtype=float) x = np.asarray(x, dtype=float) b = np.asarray(b, dtype=float) if A.ndim != 2: raise ValueError("A deve ser uma matriz 2D.") if x.ndim != 1 or x.shape[0] != A.shape[1]: raise ValueError("x deve ser um vetor 1D com comprimento igual ao número de colunas de A.") if b.ndim != 1 or b.shape[0] != A.shape[0]: raise ValueError("b deve ser um vetor 1D com comprimento igual ao número de linhas de A.") r = b - A @ x return r
[documentos] def exibir_residuo_detalhado(A, x, b): """Exibe detalhadamente as matrizes A, x, b e o resíduo r = b - A x. Útil para depuração e apresentação passo a passo da solução do sistema. """ n = len(A) largura = 18 print("\nMatrizes do cálculo do resíduo:") print("A:") for i in range(n): print(" ".join(f"{A[i,j]:>{largura}}" for j in range(n))) print("\nx:") for j in range(n): print(f"{x[j]:>{largura}}") print("\nb:") for i in range(n): print(f"{b[i]:>{largura}}") r = b - A @ x print("\nr = b - A x:") for i in range(n): print(f"{r[i]:>{largura}}") print()
def menu(): while True: print("\nMenu de métodos para resolver sistemas lineares:") print("1 - Método de Gauss sem pivotamento") print("2 - Método de Gauss com pivotamento") print("3 - Decomposição LU sem pivotamento") print("4 - Decomposição LU com pivotamento") print("0 - Sair") opcao = input("Escolha a opção desejada: ") if opcao == '0': print("Encerrando o programa.") break try: result = montar_sistema_valores() if result is None: # leitura não completada (possivelmente EOF em ambiente não interativo) print("Leitura de dados não completada. Retornando ao menu principal.") continue A, b, vars = result if opcao == '1': x, Atri, bmod, _ = eliminacao_gauss_sem_pivotamento(A, b) if x is None: print("Sistema impossível pelo método sem pivotamento.") continue print("\nSolução pelo método de Gauss sem pivotamento:") for var, val in zip(vars, x): print(f"{var} = {val}") exibir_residuo_detalhado(A, x, b) elif opcao == '2': x, Atri, bmod = eliminacao_gauss_com_pivotamento(A, b) if x is None: print("Sistema impossível pelo método com pivotamento.") continue print("\nSolução pelo método de Gauss com pivotamento:") for var, val in zip(vars, x): print(f"{var} = {val}") exibir_residuo_detalhado(A, x, b) elif opcao == '3': L, U = lu_sem_pivot(A,b) y = forward_solve(L, b) x = backward_solve(U, y) print("\nSolução pela decomposição LU sem pivotamento:") for var, val in zip(vars, x): print(f"{var} = {val}") exibir_residuo_detalhado(A, x, b) elif opcao == '4': result = lu_com_pivot(A,b) if result is None: print("Sistema impossível pela decomposição LU com pivotamento.") continue P, L, U = result b_mod = P @ b y = forward_solve(L, b_mod) x = backward_solve(U, y) print("\nSolução pela decomposição LU com pivotamento:") for var, val in zip(vars, x): print(f"{var} = {val}") exibir_residuo_detalhado(A, x, b) else: print("Opção inválida. Tente novamente.") except Exception as e: print(f"Erro: {e}") if __name__ == "__main__": menu()