9. Álgebra Linear Computacional básica#

9.1. 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;

9.2. 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.

9.3. Matrizes e vetores#

Uma matriz A de ordem m×n pode ser escrita como:

A=[a11a12a1na21a22a2nam1am2amn]

As colunas de uma matriz com m linhas correspondem a n vetores v1,v2,,vn, de maneira que

A=[v1v2vn]

é 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 R3 a seguir:

  • u=3i2j+9k

  • v=2i+4j

  • w=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

A=[uvw]

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

[224121]

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.

9.3.1. 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 A:

# modo 2: matriz transposta
At = np.array([u,v,w]).T 
print(At)
[[ 3 -2  1]
 [-2  4  0]
 [ 9  0  0]]

9.3.2. 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])

9.4. Operações fundamentais#

9.4.1. 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: u±v

# adição 
ad = u + v
print(ad)

# subtração
sub = u - v
print(sub)
[1 2 9]
[ 5 -6  9]

Exemplo: A±B, com

B=[u2u3v]
# 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]]

9.4.2. Produto interno#

O produto interno u,v é 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

Uma segunda forma, mais imediata, emprega o operador infixo @:

pii = u @ v
print(pii)

pii2 = np.array([3,1]) @ np.array([-1,-1])
print(pii2)
-14
-4

9.4.3. Norma de vetor#

A norma ||u|| de um vetor u é calculada como:

np.sqrt(np.dot(u,u))
9.695359714832659

9.4.4. Produto de matrizes#

O produto AB 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]])

9.4.5. Produto entre matriz e vetor#

Neste caso, sendo 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) uma matriz m×n e b e um vetor n×1, respectivamente, o produto Ab é dado por:

b = np.array([3,4,1])

np.dot(A,b)
array([ 2, 10, 27])

9.5. 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

9.5.1. Determinante#

O determinante de A é dado por det(A) e pode ser computado pela função det.

# calculando o determinante da matriz
det = lin.det(A)
print(det)
-36.0

9.5.2. Inversa de uma matriz#

A inversa de uma matriz é dada por A1, onde AA1=I, e 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 2.22044605e-16 0.00000000e+00]
 [1.11022302e-16 1.00000000e+00 0.00000000e+00]
 [0.00000000e+00 0.00000000e+00 1.00000000e+00]]

9.5.3. Soluções de sistemas lineares#

Para resolver sistemas lineares, devemos escrever as equações em forma de matriz, ou seja, Ax=b e utilizar solve.

Exemplo: Resolva o sistema linear abaixo:

{ax+by=cdx+ey=f

para a=4, b=1, c=1/2, d=3, e=5 e f=10,

A = np.array([[-4,1],[3, 5]])

b = np.array([1/2,10])

# solução
x = lin.solve(A, b)
print(x)
[0.32608696 1.80434783]

9.5.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.2173913   0.04347826]
 [ 0.13043478  0.17391304]]

Para realizar uma “prova real” da solução do sistema anterior, poderíamos fazer:

x2 = np.dot(lin.inv(A), b)
print(x2)
[0.32608696 1.80434783]

Note, entretanto que:

x == x2
array([ True,  True])

Isto ocorre devido a erros numéricos. Um teste mais adequado deve computar a norma do vetor “erro”, dado por e=bAx. A norma pode ser calculada diretamente com:

e = b - np.dot(A,x)
lin.norm(e)
0.0

Isto é, esperamos que ||e||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.

9.6. Algumas matrizes especiais#

9.6.1. 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.]])

9.6.2. 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.]])

9.6.3. 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.]])

9.6.4. 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]])

9.6.5. 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]])

9.7. Autovalores e autovetores#

Um vetor vV, v0 é vetor próprio de A se existir λR tal que

Av=λv.

O número real λ é denominado valor próprio (autovalor) de A associado ao vetor próprio (autovetor) 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.1400549446402586 -5.1400549446402595
[0.99033427 0.13870121]
[-0.13870121  0.99033427]

9.8. 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 

M = np.array( [ [ [ [-1,0],[1,0] ], [ [-1,0],[1,0] ]] ])

np.sum(M,axis=2)
array([[[0, 0],
        [0, 0]]])
# 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