segunda-feira, 16 de novembro de 2009

Matrizes

Todos temos ideia do que são matrizes e das diversas operações que com elas podemos fazer. Existem em Python módulos que nos permitem manipular de forma eficiente matrizes, como por exemplo Numpy and SciPy. Também o pacote Matplotlib, e o módulo nele integrado pylab, permitem efectuar operações com matrizes. Mas, admitamos que não temos e que necessitamos de definir uma representação para matrizes e implementar as operações básicas. O modo mais correcto de o fazer seria definir um novo tipo de dados e implementá-lo como uma classe. Mas isso remete-nos para a programação orientada aos objectos, território ainda por nós não explorado. Vamos então caçar com gato.


Uma matriz vai ser representada como uma lista de listas. Cada elemento será uma linha da matriz. A partir desta decisão, as implementações das operações elementares decorrem naturalmente:


def addMatrix(A,B):
""" Soma duas matrizes."""
sizeL=len(A)
sizeC=len(A[0])
C=nullMatrix(sizeL,sizeC)
# Soma
for i in range(sizeL):
for j in range(sizeC):
C[i][j]=A[i][j]+B[i][j]
return C

def prodMatrix(A,B):
"""Multiplica duas matrizes."""
sizeL=len(A)
sizeC=len(A[0])
C=nullMatrix(sizeL,sizeC)
# Multiplica
for i in range(sizeL):
for j in range(sizeC):
val=0
for k in range(len(B[0])):
val = val + A[i][k]*B[k][j]
C[i][j]=val
return C

def transposeMatrix(M):
"""Calcula a transposta de uma matriz."""
aux=[]
for j in range(len(M[0])):
linha=[]
for i in range(len(M)):
linha.append(M[i][j])
aux.append(linha)
return aux

Como se nota estas operações têm todas uma forma semelhante, envolvendo um ciclo dentro de outro ciclo, isso mesmo consequência da representação escolhida. Para testar estas operações podemos definir operações auxiliares:

import random

def cria_matriz(lin,col):
A=[]
for i in range(lin):
linha=[]
for j in range(col):
linha = linha + [random.randint(1,10)]
A= A + [linha]
return A

def mostra_matriz(matriz):
print 'Matriz'
for i in range(len(matriz)):
for j in range(len(matriz[0])):
print matriz[i][j],'\t',
print
print '_' * 10

E pronto. Divirta-se!

13 comentários:

  1. Lembrando que essa transposta só funciona com matriz quadrada.

    ResponderEliminar
    Respostas
    1. Caro Guilherme,

      Não é verdade o que diz. Por definição qualquer matriz, quadrada ou não, tem uma transposta: a coluna i passa a linha i. O programa acima faz isso mesmo.
      Já agora apresento-lhe uma versão mais pitónica para o mesmo problema.

      def transposta_b(mat):
      """Transposta de uma matriz."""
      return [list(linha) for linha in zip(*mat)]

      Boas programaçaões.
      EC

      Eliminar
  2. Ajuda bastante para fazer programas mais complexos cujos passos supracitados são a base. Muito útil! Obrigado.

    ResponderEliminar
  3. como que eu encontro o determinante de uma matriz ?

    ResponderEliminar
  4. como que eu posso encontrar o determinante de uma matriz ?

    ResponderEliminar
  5. O determinante de uma matriz depende da dimensão da matriz. tirando o caso n=1 que é trivial, os casos n=2 e n=3 têm umas fórmulas simples. Por exemplo, para uma matriz 2X2 temos det(m) = a11*a22 - a21*a12. Para n>3 usa-se um processo de redução mais complexo. Se fizer uma consulta na Wikipédia encontrará as fórmulas. Em Python a forma mais simples de calcular o determinante é usar o método det do módulo linalg do Numpy: numpy.linalg.det(M).

    ResponderEliminar
  6. uma função tão simples e tão usada (somar matrizes) e é preciso escrever toda uma rotina para o python fazer isto...

    ResponderEliminar
  7. É verdade o que diz e eu também o disse. Acontece que este é um blogue de suporte a uma cadeira de introdução à programação e por isso nos ficamos pelos conceitos básicos. Neste caso a ideia de usar dois ciclos para estruturas bi-dimensionais. Mas está à vontade para enviar outra solução mais simples.

    ResponderEliminar
  8. Ernesto, vi que seus código para criar e mostrar matrizes possuem alguns erros (pelo menos o que mostra o teste de mesa), fiz algumas correções:
    def cria_matriz(lin,col):
    """Cria uma matriz aleatória com elementos de valores entre 0 e 10"""
    import random
    global A
    A=[]
    for i in range(lin):
    linha=[]
    for j in range(col):
    linha.append(random.randint(0,10)) A.append(linha)
    return A

    def mostra_matriz(matriz):
    print 'Matriz'
    for i in range(len(matriz)):
    for j in range(len(matriz[0])):
    print matriz[i][j],'\t',
    print "\n"
    print '_' * 10

    ResponderEliminar
  9. A linha C=nullMatrix(sizeL,sizeC) da multiplicação de Matrizes está com erro , eu a reescrevi como C=[[0]*sizeC]*sizeL .

    Mas a Matriz Resultante não está correta . Ela está apresentando todos os valores de uma coluna iguais , verificando o código com impressões vi que quando um elemento é alterado , todos os elementos da coluna do elemento também são alterados .

    Também verifiquei que sizeC=len(A[0]) é para ser sizeC=len(B[0]) pois a quantidade de colunas da matriz resultante é igual a quantidades de colunas da segunda matriz .

    ResponderEliminar
    Respostas
    1. O código nullMatrix não foi fornecido. A maneira como faz dá erro devido ao facto de Python fazer partyilha de memória e estar a trabalhar com listas de listas. Um modo de evitar o problema é fazer:

      C = [[0 for i in range(len(B[0])] for i in range(len(A))]

      Tem razão quanto ao facto de não ser A[0] mas sim B[0].

      Eliminar
  10. Caro rick,

    Não sei que erro lhe poder ter dado, pois as rotinas estão correctas. Já agora duas notas. É boa prática de programação as importações estarem fora das definições e no início dos ficheiros que as contém. Outra caso é o da declaração de A como global, que só devemos usar quando absolutamente necessário, o que não é o caso.

    ResponderEliminar