Álgebra Linear Computacional básica¶
Objetivos¶
Associar conceitos abstratos de Álgebra Linear a estruturas computacionais;
Realizar operações básicas com vetores e matrizes;
Saber como resolver sistemas lineares de pequeno porte;
Calcular autovalores e autovetores em matrizes reais;
Introdução¶
Neste capítulo, mostraremos como realizar operações básicas entre matrizes e vetores usando o computador. A manipulação de matrizes e vetores é essencial em muitas ciências, principalmente para resolver sistemas lineares.
As matrizes são utilizadas na computação para armazenar informações bidimensionais. Em particular, podem representar translação, rotação, escalonamento e sistemas de equações. O estudo da relação entre algoritmos e métodos computacionais para trabalhar eficientemente com matrizes e vetores é realizado no âmbito da Álgebra Linear Computacional.
Matrizes e vetores¶
Uma matriz \({\bf A}\) de ordem \(m \times n\) pode ser escrita como:
As colunas de uma matriz com \(m\) linhas correspondem a \(n\) vetores \(\vec{v}_1, \vec{v}_2, \ldots,\vec{v}_n\), de maneira que
é uma representação equivalente para a matriz anterior.
Em Python, usamos o módulo numpy para trabalhar com matrizes e vetores. Vetores são arrays 1D, ao passo que matrizes são arrays 2D, ou seja, um “array de arrays”.
Exemplo. Represente computacionalmente os vetores do \(\mathbb{R}^3\) a seguir:
\(\vec{u} = 3\vec{i} - 2\vec{j} + 9\vec{k}\)
\(\vec{v} = -2\vec{i} + 4\vec{j}\)
\(\vec{w} = \vec{i}\)
Note
O NumPy possui uma classe especial para se trabalhar com matrizes e vetores em uma ou duas dimensões, a saber o tipo matrix
, ou mat
. Com objetos matrix
, as operações particulares de multiplicação matriz-matriz ou matriz-vetor comportam-se diferentemente daquelas na classe ndarray
. Neste texto, abordaremos apenas os tipos ndarray
porque são aplicáveis também a matrizes multidimensionais.
import numpy as np
u = np.array([3,-2,9])
v = np.array([-2,4,0])
w = np.array([1,0,0])
print(u), print(v), print(w);
[ 3 -2 9]
[-2 4 0]
[1 0 0]
Exemplo. Represente computacionalmente a matriz 3 x 3 dada por
Observe que os vetores devem ser escritos como “coluna”.
A = np.array([[3,-2,1],[-2,4,0],[9,0,0]])
print(A)
[[ 3 -2 1]
[-2 4 0]
[ 9 0 0]]
Exemplo. Represente computacionalmente a matriz
Vamos escrever linha por linha.
L1 = np.array([2,-2]) # linha 1
L2 = np.array([4,1]) # linha 2
L3 = np.array([2,1]) # linha 3
A2 = np.array([L1,L2,L3]) # lista de listas
print(A2)
[[ 2 -2]
[ 4 1]
[ 2 1]]
Diretamente, poderíamos também definir:
A3 = np.array([[2,-2],[4,1],[2,1]])
print(A3)
[[ 2 -2]
[ 4 1]
[ 2 1]]
Note que cada lista representa uma linha.
Transposição¶
Matrizes e vetores podem ser transpostos com .T
:
A2T = A2.T
print(A2T)
[[ 2 4 2]
[-2 1 1]]
Assim, com as variáveis antes definidas, poderíamos, equivalentemente, fazer para \({\bf A}\):
# modo 2: matriz transposta
At = np.array([u,v,w]).T
print(At)
[[ 3 -2 1]
[-2 4 0]
[ 9 0 0]]
Teste de igualdade¶
Podemos verificar a igualdade entre matrizes como
A == At
array([[ True, True, True],
[ True, True, True],
[ True, True, True]])
No caso de vetores:
# vetor "linha" não difere
# do vetor "coluna"
u == u.T
array([ True, True, True])
Operações fundamentais¶
Adição e subtração¶
A adição (subtração) de matrizes e vetores pode ser realizada de modo usual com computação vetorizada.
Exemplo: \(\vec{u} \pm \vec{v}\)
# adição
ad = u + v
print(ad)
# subtração
sub = u - v
print(sub)
[1 2 9]
[ 5 -6 9]
Exemplo: \(\bf{A} \pm \bf{B}\), com
# adição
B = np.array([u,2*u,3*v]).T
ad2 = A + B
print(ad2)
sub2 = A - B
print(sub2)
[[ 6 4 -5]
[-4 0 12]
[18 18 0]]
[[ 0 -8 7]
[ 0 8 -12]
[ 0 -18 0]]
Produto interno¶
O produto interno \(\langle \vec{u}, \vec{v}\rangle\) é computado com .dot
:
pi = np.dot(u,v)
print(pi)
pi2 = np.dot(np.array([3,1]),np.array([-1,-1]))
print(pi2)
-14
-4
Norma de vetor¶
A norma \(||\vec{u}||\) de um vetor \(\vec{u}\) é calculada como:
np.sqrt(np.dot(u,u))
9.695359714832659
Produto de matrizes¶
O produto \(\bf{A}\bf{B}\) entre matrizes bidimensionais pode ser calculado com np.dot
, mas recomenda-se usar np.matmul
.
# não tem o mesmo efeito para
# matrizes A e B de tamanhos arbitrários
np.dot(A,B)
array([[ 22, 44, -42],
[-14, -28, 60],
[ 27, 54, -54]])
# uso recomendado para a operação tradicional
np.matmul(A,B)
array([[ 22, 44, -42],
[-14, -28, 60],
[ 27, 54, -54]])
Produto entre matriz e vetor¶
Neste caso, sendo \({\vec{\vec A}}\) (dois símbolos indicam que a matriz é uma grandeza de ordem 2, ao passo que o vetor é de ordem 1 e aqui usamos para consistência de notação) e \({\vec{b}}\) uma matriz \(m \times n\) e um vetor \(n \times 1\), respectivamente, o produto \(\vec{\vec{A}}\vec{b}\) é dado por:
b = np.array([3,4,1])
np.dot(A,b)
array([ 2, 10, 27])
Demais operações com numpy.linalg
¶
Para outras operações, devemos utilizar o submódulo numpy.linalg
. Para importá-lo com o alias lin
, fazemos:
import numpy.linalg as lin
Determinante¶
O determinante de \({\bf A}\) é dado por \(\det({\bf A})\) e pode ser computado pela função det
.
# calculando o determinante da matriz
det = lin.det(A)
print(det)
-36.0
Inversa de uma matriz¶
A inversa de uma matriz é dada por \({\bf A}^{-1}\), onde \({\bf A}{\bf A}^{-1}={\bf I}\), e \({\bf I}\) é a matriz identidade. Para usar esta função, devemos fazer:
B2 = np.array([[1,2,3],
[2,3,4],
[1,2,0]])
B3 = lin.inv(B2)
print(np.matmul(B3,B2))
[[ 1.00000000e+00 -5.55111512e-16 0.00000000e+00]
[ 1.11022302e-16 1.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 0.00000000e+00 1.00000000e+00]]
Soluções de sistemas lineares¶
Para resolver sistemas lineares, devemos escrever as equações em forma de matriz, ou seja, \({\vec{\vec A}}\vec{x} = \vec{b}\) e utilizar solve
.
Exemplo: Resolva o sistema linear abaixo:
para \(a = -4\), \(b = 1\), \(c = 1/2\), \(d = 3\), \(e = 5\) e \(f = 10\),
A = np.array([[-4,1],[1/2,3]])
b = np.array([5,10]).T
# solução
x = lin.solve(A, b)
print(x)
[-0.4 3.4]
Inversa de matriz¶
A inversa de uma matriz (faça esta operação apenas para matrizes quadradas de pequena dimensão) pode ser encontrada como:
Ainv = lin.inv(A)
print(Ainv)
[[-0.24 0.08]
[ 0.04 0.32]]
Para realizar uma “prova real” da solução do sistema anterior, poderíamos fazer:
x2 = np.dot(lin.inv(A), b)
print(x2)
[-0.4 3.4]
Note, entretanto que:
x == x2
array([False, False])
Isto ocorre devido a erros numéricos. Um teste mais adequado deve computar a norma do vetor “erro”, dado por \({\bf e} = \bf{b} - \bf{A}\bf{x}\). A norma pode ser calculada diretamente com:
e = b - np.dot(A,x)
lin.norm(e)
0.0
Isto é, esperamos que \(||{\bf e}|| \approx 0\) quando a solução do sistema for exata, a menos de erros numéricos.
Warning
Nunca compare dois números reais (float
) usando igualdade. Ou seja, x == y
, não é, em geral, um bom teste lógico para verificar se x
e y
possuem o mesmo valor numérico.
Algumas matrizes especiais¶
Nula¶
Para criar uma matriz nula de ordem m x n, usamos zeros
.
m,n = 3,4
np.zeros((m,n))
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
Identidade¶
Uma matriz identidade (quadrada) de ordem p é criada com eye
.
p = 4
np.eye(p)
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
Matriz de “uns”¶
Uma matriz composta apenas de valores 1 de ordem m x n pode ser criada com ones
:
np.ones((3,5))
array([[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]])
Triangular inferior¶
A matriz triangular inferior de uma dada matriz pode ser criada com tril
. Note que podemos também defini-la explicitamente, linha a linha.
# os valores correspondentes
# são zerados
np.tril(B)
array([[ 3, 0, 0],
[-2, -4, 0],
[ 9, 18, 0]])
Triangular superior¶
A matriz triangular superior de uma dada matriz pode ser criada com triu
. Note que podemos também defini-la explicitamente, linha a linha.
np.triu(B)
array([[ 3, 6, -6],
[ 0, -4, 12],
[ 0, 0, 0]])
Exercício. Por que há dois valores False
no teste a seguir?
B == np.tril(B) + np.triu(B)
array([[False, True, True],
[ True, False, True],
[ True, True, True]])
Autovalores e autovetores¶
Um vetor \({\bf v} \in V\), \({\bf v} \neq {\bf 0}\) é vetor próprio de \({\bf A}\) se existir \(\lambda \in \mathbb{R}\) tal que
O número real \(\lambda\) é denominado valor próprio (autovalor) de \({\bf A}\) associado ao vetor próprio (autovetor) \({\bf v}\).
A = np.array([[2,1],
[1,-5]])
w, v = lin.eig(A)
a,b = w
# autovalores
print(a,b)
# autovetor 1
print(v[:,0])
# autovetor 2
print(v[:,1])
2.1400549446402595 -5.1400549446402595
[0.99033427 0.13870121]
[-0.13870121 0.99033427]
Somas e valores extremos¶
Podemos calcular somas de elementos de matrizes e vetores de maneiras diferentes. Para matrizes, em particular, há soma total, por linha, ou por coluna.
a = np.array([1,-2,-3,10])
# soma de todos os elementos
np.sum(a)
6
# modo alternativo
a.sum()
6
# soma total de matriz
O = np.ones((5,3))
np.sum(O)
15.0
# modo alternativo
O.sum()
15.0
# soma por linha
np.sum(O,axis=0)
array([5., 5., 5.])
# soma por coluna
np.sum(O,axis=1)
array([3., 3., 3., 3., 3.])
Valores máximos e mínimos, absolutos ou não, também podem ser computados com funções simples.
# min
np.min(a)
-3
# max
np.max(a)
10
# modo alternativo
a.min()
-3
a.max()
10
# mínimo absoluto
np.abs(a).min()
1
# máximo absoluto
np.abs(a).max()
10
O2 = np.array([[-4,5],[2,7]])
# min
np.min(O2)
-4
# max
np.max(O2)
7
O2.min()
-4
O2.max()
7
np.abs(O2).min()
2
np.abs(O2).max()
7