Você está na página 1de 15

Universidade Federal de Ouro Preto

Instituto de Ciências Exatas e Biológicas

Departamento de Computação

ALGORITMOS E ESTRUTURAS DE DADOS


Matrizes Esparsas
Segundo Trabalho disponível em:
http://www.decom.ufop.br/prof/menotti/aedI082/tps/tp2.pdf

Brayan Vilela Alves Neves

Professor - David Menotti

Ouro Preto
30 de março de 2009
Sumário
1 Introdução 1
2 Algoritmo e estruturas de dados 2
2.1 Estrutura de TMatriz . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Insere/Remove . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3 Função Get . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Imprime/Soma/Multiplica . . . . . . . . . . . . . . . . . . . . . . . . 8

3 Análise de complexidade dos algoritmos 9


3.1 fazMatrizVazia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 criaMatriz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Vazia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.4 insere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.5 remove . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.6 leMatriz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.7 get . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.8 imprimeMatriz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.9 somaMatriz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.10 multiplicaMatriz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

4 Conclusão 12

2
1 Introdução
Neste trabalho temos a apresentação de um problema na computação. Que é
a representação de uma grande quantidade de valores em uma matriz, sem se
preocupar em ocupar espaço com os valores que são nulos (conceito da ultilização
de Matrizes Esparsas).
Imagine uma mina de ouro. Agora imagine que nem toda a mina exista ouro.
Então se separarmos esta mina em seções, e cada seção, por exemplo, colocamos a
concentração de ouro que exista nela. Tendo que o ouro é um mineral muito raro
(supondo que o ouro domina cerca de 30% da área da mina), logo percebemos que a
maioria das nossas seções terá concentração igual à zero. Então logo nos vem uma
pergunta: "Então não seria inviável representar esta mina por seções já que pelo
menos 70% delas são nulas?".

Este é um dos casos onde podemos aplicar nosso conceito Matriz Esparsa. Uma
matriz esparsa irá representar apenas as posições válidas, sendo assim, analisando
o caso da mina de ouro citada acima, nosso banco de dados irá ter apenas 30% do
que teria se fosse representar toda a mina, incluindo as partes com concentração de
ouro nula.

Em nosso Trabalho iremos apresentar apenas matrizes bidimensionais, tendo em


vista que a principio que a base de nossa matriz é uma lista encadeada, temos todas
as funções básicas da mesma e outras para operações entre matrizes:

• Faz Matriz Vazia (cria e inicializa o elemento principal da Matriz Esparsa)

• Vazia (retorna se a matriz já foi criada ou não)

• Cria Matriz (gera o esqueleto de nossa matriz)

• Insere (insere um elemento na matriz)

• Remove (remove um elemento da matriz)

• Lê Matriz (recebe um endereço de um arquivo na memória do computador e


lê a matriz a partir dele)
• Get (lê o valor de um item da matriz)

• Imprime Matriz (imprime a matriz na tela, incluindo as posições nulas)

• Soma Matriz (retorna uma matriz que é a soma de outras duas)

• Multiplica Matriz (retorna uma matriz que é a multiplicação de outras duas)

O trabalho está organizado da seguinte forma. Na Seção 2 são apresentadas as


estruturas de dados utilizadas para os algoritmos e uma descrição sobre a implementação
daa bibliotecaa. Na Seção 3 são apresentadas as analises de complexidade dos
algoritmos utilizados. E nalmente na Seção 4, as conclusões são apontadas.

1
2 Algoritmo e estruturas de dados
2.1 Estrutura de TMatriz

Nesta seção é apresentada a estrutura de dados, bem como uma leve explicação
sobre a interface do programa construído. A estrutura básica da matriz esparsa,
denida como TMatriz contem um elemento chave:

Listagem 1: Estrutura de uma celula de TMatriz


class Celula {

public :
Apontador direita , abaixo ;

int l i n h a , c o l u n a ;
5 double v a l o r ;
}; /∗ C e l u l a ∗/

typedef struct Celula ∗ Apontador ;

Uma TMatriz Esparsa é composta por células, cada uma delas armazena Apontadores
(uma estrutura que na verdade é um ponteiro para células) para as células da direita
e para a célula abaixo, além disso, também guardam os valores inteiros, linha e
coluna, que indicam a posição em que a célula se encontra na matriz, além de uma
variável tipo double que guarda o valor desta célula.
Então com as células denidas, temos a estrutura de TMatriz:

Listagem 2: Estrutura de TMatriz


c l a s s TMatriz {

public :
Apontador cabeca ;

int m l i n h a ;
5 int m c o l u n a ;
void f a z M a t r i z V a z i a ( void ) ;
int V a z i a ( void ) ;
void c r i a M a t r i z ( int l i n h a , int c o l u n a ) ;
void i m p r i m e M a t r i z ( void ) ;
10 void i n s e r e ( int l i n h a , int c o l u n a , double valor ) ;

int r e m o v e ( int l i n h a , int c o l u n a ) ;


double g e t ( int l i n h a , int c o l u n a ) ;
void l e M a t r i z ( char ∗ l o c a l ) ;
}; / ∗ TMatriz ∗ /

A estrutura de TMatriz é na verdade uma célula cabeça, e dois inteiros que


indicam o tamanho vertical (mlinha) e o tamanho horizontal (mcoluna) da matriz,
além das funções já citadas anteriormente na introdução.

Então vamos inicializar esta matriz da seguinte forma. Indicamos quantas linhas
e quantas colunas a matriz terá e chamamos o código criaMatriz( int linha, int
coluna ):

Listagem 3: Função que cria a estrutura de TMatriz

2
void TMatriz : : f a z M a t r i z V a z i a ( ) {

cabeca = ( Apontador ) malloc ( sizeof ( Celula ) ) ;


cabeca −>a b a i x o = c a b e c a ;
c a b e c a −> d i r e i t a = c a b e c a ;

5 c a b e c a −>l i n h a = − 1;

c a b e c a −>c o l u n a = − 1;

} /∗ f a z M a t r i z V a z i a ∗/

void T M a t r i z : : criaMatriz ( int linha , int coluna ) {

10 int i ;
fazMatrizVazia () ;

mlinha = linha ;

mcoluna = coluna ;

Apontador aux = cabeca ;

15 for ( i = 0; i < linha ; i++ ) {

aux−> d i r e i t a = ( A p o n t a d o r ) malloc ( sizeof ( Celula ) ) ;


aux = aux−> d i r e i t a ;

aux−> d i r e i t a = c a b e c a ;

aux−>a b a i x o = aux ;

20 aux−>c o l u n a = − 1;

} /∗ f o r ∗/
aux = cabeca ;

for ( i = 0; i < coluna ; i++ ) {

−>a b a i x o = ( A p o n t a d o r )
aux malloc ( sizeof ( Celula ) ) ;
25 aux = aux−>a b a i x o ;

aux−> d i r e i t a = aux ;

aux−>a b a i x o = c a b e c a ;

aux−>l i n h a = − 1;

} /∗ f o r ∗/
30 } /∗ c r i a M a t r i z ∗/

Quando chamamos a função que cria o esqueleto de nossa matriz, ela automaticamente
a função fazMatrizVazia(), que inicializa a célula cabeça de nossa matriz, então
criamos a chave de uma lista circular, fazendo a célula cabeça apontar para ela
mesmo tanto pra direita quanto para a esquerda e o seu valor de linha e coluna
inicializado com -1, para futuro controle da função.

Figura 1: Célula Cabeça inicializada

Com a célula cabeça inicializada, a função seta qual será o número de linhas e
colunas a matriz terá e cria um Apontador para cabeça de nossa função. Então
através de um laço de repetição ele cria quantas colunas à matriz terá, alocando
espaço para a célula de controle de cada coluna, fazendo a célula a esquerda dela
apontar para ela, e para manter a lista circular, faz a célula apontar para a cabeça,
depois volta o Apontador para a cabeça e setando a variável de controle de coluna
para -1, e fazendo o mesmo procedimento cria o número de linhas, mas movendo os
ponteiros abaixo e mudando a variável de controle linha para -1.

3
Figura 2: Matriz 2x2 inicializada

Para comparar se uma Matriz é vazia, ele checa se a matriz é uma célula cabeça:

Listagem 4: Determina se uma matriz é vazia


int TMatriz : : Vazia ( ) {

return ( cabeca −>a b a i x o == cabeca −> d i r e i t a ) ;

} / ∗ Vazia ∗ /

Levando em conta que uma Matriz inicializada, mesmo contendo apenas valores
nulos (zero), não é vazia.

2.2 Insere/Remove

Para inserir um elemento na matriz, informamos apenas as coordenadas onde ele


será inserido e o valor da célula e temos:

Listagem 5: Iserindo item na matriz


void T M a t r i z : : insere ( int linha , int coluna , double valor ) {

int i ;
int p a r a = 0;

Apontador Item ;

5 Item = ( Apontador ) malloc ( sizeof ( Celula ) ) ;


Item −>v a l o r = v a l o r ;
I t e m −>l i n h a = l i n h a ;

I t e m −>c o l u n a = c o l u n a ;

Apontador aux = cabeca ;

10 for ( i = 0; i < coluna ; i++ ) {

aux = aux −> d i r e i t a ;

} /∗ f o r ∗/

while ( p a r a == 0 ) {
15 i f ( p a r a == 0 && ( aux−>a b a i x o −>c o l u n a == −1 || −>a b a i x o −>l i n h a
aux

>= linha ) ) {

Item −>a b a i x o = aux−>a b a i x o ;


aux −>a b a i x o = I t e m ;
para = 1;

4
} /∗ i f ∗/
20 else
aux = aux−>a b a i x o ;

} /∗ w h i l e ∗/
aux = cabeca ;

for ( i = 0; i < linha ; i++ ) {

25 aux = aux −>a b a i x o ;


} /∗ f o r ∗/
para = 0;

while ( p a r a == 0 ) {
i f ( p a r a == 0 && ( aux−> d i r e i t a −>l i n h a == −1 || −> d i r e i t a −>
aux

c o l u n a >= coluna ) ) {

30 Item −> d i r e i t a = aux−> d i r e i t a ;

aux −> d i r e i t a = I t e m ;
para = 1;

} /∗ i f ∗/
else {

35 aux = aux −> d i r e i t a ;

} /∗ w h i l e ∗/

} /∗ i n s e r e ∗/

Primeiro aloca-se a célula e seta seus atributos linha, coluna e valor, depois se cria
um Apontador que apontara para cabeça da matriz. Então ele andara para direita
até a coluna determinada, a partir daí ele terá que achar a linha correta fazendo
comparações, se o valor de controle de coluna for igual a -1 ou maior ou igual ao
determinado, ele ira setar a posição ali, fazendo a manipulação de Apontadores:

• A nova célula aponta (abaixo) para o item abaixo (Célula sucessora ou esqueleto).
• A célula Apontada seta seu apontador (abaixo) para nova célula.

Supondo que chamamos o comando insere(1,1,50), temos até agora:

Figura 3: Inserindo célula - Parte I

Agora fazendo processos análogos aos processos acima, trocando onde for linha
por coluna e onde for abaixo por direita. Temos:

5
Figura 4: Inserindo célula - Parte II

Com o item inserido no lugar correto, através da função remove, que é o processo
inverso da insere, podemos retirar a célula valida do local:

Listagem 6: Removendo item da matriz


int TMatriz : : remove ( int linha , int coluna ) {

int i ;

Apontador aux = cabeca ;

for ( i = 0; i < coluna ; i++ ) {

5 aux −> d i r e i t a
= aux ;

} /∗ f o r ∗/
for ( i = 0; i < linha ; i++ ) {

if ( −>a b a i x o −>l i n h a == l i n h a ) {
aux

aux−>a b a i x o = aux−>a b a i x o −>a b a i x o ;

10 aux = cabeca ;

for ( i = 0; i < linha ; i++ )

aux = aux −>a b a i x o ;


for ( i = 0; i < coluna ; i++ )

if ( −> d i r e i t a −>c o l u n a == c o l u n a ) {
aux

15 aux−> d i r e i t a = aux−> d i r e i t a −> d i r e i t a ;

return 1;

} /∗ i f ∗/
else {

= aux−> d i r e i t a
aux ;

20 /∗ e l s e ∗/
}

} /∗ i f ∗/
else {

= aux−>a b a i x o ;
aux

/∗ e l s e ∗/
}

25 } /∗ f o r ∗/
return 0;

} / ∗ remove ∗ /

A diferença é que primeiro só recebemos no parâmetro a posição da célula a ser


removida. Então se procura a célula tanto por cima quanto pela direita, seta a célula
anterior apontar para a próxima depois da selecionada e destrói a célula selecionada.

6
Também temos a opção de ler um bloco de células de uma só vez, através da
função leMatriz( char* ):

Listagem 7: Função que lê um bloco de células a partir de um arquivo


void TMatriz : : l e M a t r i z ( char ∗ l o c a l ) {

FILE ∗ arq = fopen ( l o c a l , "r" ) ;


5 int m, n , i , j ;
double v a l o r ;
fscanf ( arq , "%d, %d" ,&m,& n ) ;
criaMatriz ( m, n ) ;

i f ( arq )
10 while ( ! f e o f ( arq ) ) {

fscanf ( arq , "%d, %d, %lf" ,& i ,& j ,& v a l o r ) ;


insere ( i , j , valor ) ;

} /∗ w h i l e ∗/
f c l o s e ( arq ) ;

15 } / ∗ LeMatriz ∗ /

Que recebe uma string com um endereço do disco rígido contendo um arquivo
que tenha as informações no formato correto:

2, 2
1, 1, 50.0
2, 1, 10.0

A primeira linha do arquivo é uma dupla com o numero de linhas e colunas da


matriz, e o restante do arquivo de triplas com as coordenadas do próximo ponto e o
valor da célula correspondente.
Após chamar a função com o arquivo acima temos a matriz:

Figura 5: Matriz lida a partir do arquivo

7
2.3 Função Get

Esta é a função chave para a resolução do resto do programa:

Listagem 8: Retorna valor da posição dada


double TMatriz : : g e t ( int linha , int coluna ) {

Apontador aux = cabeca ;

for ( int i = 0; i < linha ; i++ ){

aux = aux −>a b a i x o ;


5 } /∗ f o r ∗/
for ( int i = 0 ; i < c o l u n a ; i++ ){

i f ( aux−> d i r e i t a −>c o l u n a != coluna )

aux = aux −> d i r e i t a ;

else
10 return aux −> d i r e i t a −>v a l o r ;
} /∗ f o r ∗/
return 0;

} /∗ g e t ∗/

A função get(int, int), após dar o valor da linha e da coluna, ela retorna o valor
da célula, e caso ela não exista, retorna o numero zero que corresponde que não
existe a célula procurada na matriz, e logo por ela ser nula seu valor é zero.

2.4 Imprime/Soma/Multiplica

Com o auxilio da função Get, usaremos apenas simples algoritmos de Impressão,


soma e multiplicação de matrizes para resolver tais problemas[1]

Listagem 9: Simples altoritmos de manipulação de matrizes usando a função Get


void T M a t r i z : : i m p r i m e M a t r i z ( ) {

int i , j ;
printf ( "\n" ) ;
for ( i = 1 ; i < m l i n h a + 1 ; i++ ) {

5 for ( j = 1 ; j < m c o l u n a + 1 ; j++ )

i f ( g e t ( i , j ) == 0 )
printf ( "0\t\t" ) ;
else
printf ( "%lf\t" , g e t ( i , j )) ;

10 printf ( "\n" ) ;
} /∗ f o r ∗/

p r i n t f ( "\n" ) ;

} / ∗ imprimeMatriz ∗ /

15 TMatriz somaMatriz ( TMatriz A, TMatriz B ) {

i f (A . m l i n h a == B . m l i n h a && A . m c o l u n a == B . m c o l u n a ) {

TMatriz C;

C. c r i a M a t r i z ( A. mlinha , A . mcoluna ) ;

int i , j ;
20 for ( i = 1 ; i < C . m l i n h a +1; i++ )

for ( j = 1; j < C . mcoluna +1; j++ )

C . i n s e r e ( i , j , ( A . g e t ( i , j )+B . g e t ( i , j ) ) ) ;

return C;

8
} /∗ i f ∗/
25 else
"Não é possivel somar matrizes de dimensoes diferentes
printf (

.\n" ) ;
} / ∗ somaMatriz ∗ /

TMatriz multiplicaMatriz ( TMatriz A, TMatriz B ) {

30 if ( A . m c o l u n a == B . m l i n h a ) {

TMatriz C;

C. c r i a M a t r i z ( A. mlinha , B . mcoluna ) ;

int i , j ;
for ( i = 1 ; i < C . m l i n h a + 1 ; i++ )

35 for ( j = 1 ; j < C . m c o l u n a + 1 ; j++ ) {

double k = 0 ;
for ( int soma = 1 ; soma < C . mcoluna +1; soma++ )

k += A . g e t ( i , soma ) ∗B . g e t ( soma , j ) ;

C. i n s e r e ( i , j , k ) ;

40 } /∗ f o r ∗/
return C;

} /∗ i f ∗/
else
printf ( "O numero de colunas da primeira Matriz tem que ser
igual ao numero de linhas da segunda Matriz .\n" ) ;
45 } /∗ m u l t i p l i c a M a t r i z ∗/

3 Análise de complexidade dos algoritmos


3.1 fazMatrizVazia

A função fazMatrizVazia(), conta apenas com uma alocação de memória e 4 atribuições,


sendo assim:

O(5) O(1).

3.2 criaMatriz

A função criaMatriz( int n, int m ), contem uma chama da função fazMatrizVazia(


O(1) ), 3 atribuições, um for de tamanho n com 5 atribuições, mais uma atribuição
e outro for de tamanho m de 5 atribuições:

O(4) + ni=0 5 + O(1) +


P Pm
i=0 5
O(5 + 5n + 5m)
O(n + m)
Se, n > ou = m :
O(n).
Se, m > n :
O(m).

9
3.3 Vazia

A função Vazia(), conta com apenas uma comparação:

O(1).

3.4 insere

A função insere( int n, int m, double x ), começa com 6 atribuições, um for de


tamanho m com uma atribuição, um while que no pior caso tem tamanho n, contendo
um if de 3 atribuições no pior caso, uma atribuição, um outro for com tamanho n,
outra atribuição e de m mais um while de tamanho m e com um if de 3 atribuições
no pior caso.

O(6) + m n n
P P P Pm
i=0 1 + i=0 4 + O(1) + i=0 1 + O(1) + i=0 4.
O(8 + m + 4n + n + 4m) = O(8 + 5n + 5m)
O(n + m)
Se, n > ou = m :
O(n).
Se, m > n :
O(m).

3.5 remove

A função remove( int n, int m), conta com uma atribuição, um for de tamanho
m de uma atribuição, um for de tamanho n com um if, que no pior caso tem 2
comparações e 2 for, um de tamanho n com uma atribuição e outro de tamanho m
com uma atribuição também.

O(1) + m n
P P Pn Pm
i=0 1 + i=0 (2 + i=0 1+ i=0 1)
2
O(1 + m + 2n + n + nm)
O(n + m + nm + n2 )
Se, n > ou = m :
O(n2 ).
Se, m > n :
O(nm).

3.6 leMatriz

A função leMatriz( char* f ), conta com 3 atribuições, uma chamada de criaMatriz(


int n, int m ) (O(n + m)), um if que no pior caso tem um while de tamanho f com
3 atribuições e uma chamada de insere( int i, int j, double v ) O(i + j)

O(3) + O(n + m) + fi=0 (3 + O(i + j))


P

O(3 + n + m + 3f + f i + f j)

10
O(n + m + f + f i + f j)
O(n + m + f i + f j)

Podendo ter 4 respostas diferentes, de acordo com o tamanho das entradas, se


n for maior será O(n), se m for maior será O(m), se (f*i) for maior será O() e por
m se (f*j) for maior será O(fj).

3.7 get

A função get( int n, int m ), tem uma atribuição e 2 for, o primeiro com tamanho n
e 1 atribuição e outro de tamanho m com um if com uma atribuição no pior caso.

O(1) + ni=0 1 +
P Pm
i=0 1
O(1 + n + m)
Se, n > ou = m :
O(n).
Se, m > n :
O(m).

3.8 imprimeMatriz

A função imprimeMatriz(), começa com uma atribuição tem um for de tamanho


mlinha (l) com outro for em cascata de tamanho mcoluna (c) com um if de uma
atribuição no pior caso.
Pl Pc
O(1) + i=0 ( i=0 1)
O(1 + l ∗ c)
O(lc).

3.9 somaMatriz

A função somaMatriz( TMatriz a, TMatriz b ), conta com 1 if de 2 comparações


que no pior caso, chama a função criaMatriz( int n, int m ) (O(n + m)) e com 2 for
em cascata, o primeiro de tamanho n e o segundo de tamanho m com uma chamada
de insere( int i, int j, double x ) O(i + j), sendo o valor maximo de i, n e o valor
maximo de j, m, no nal do segundo.

O(2) ∗ (O(n + m) + ni=0 ( m


P P
Pn i=0 (O(i + j))))
O(2) ∗ (O(n + m) + i=0 (O(m ∗ n + m2 )))
O(2) ∗ (O(n + m + mn2 + nm2 ))
O(2n + 2m + 2mn2 + 2nm2 )
O(n + m + mn2 + nm2 )
O(mn2 + nm2 )
Se, n > ou = m :
O(mn2 ).

11
Se, m > n :
O(nm2 ).

3.10 multiplicaMatriz

A função multiplicaMatriz( TMatriz a, TMatriz b ), tem um if que no pior caso tem


a chamada de criaMatriz( int n, int m ) (O(n + m)), um for de tamanho n com
um for em cascata de tamanho m com uma atribuição e outro for em cascata de
tamanho m com uma chamada de insere( int i, int j, double x ) O(i + j), sendo o
valor maximo de i, n e o valor maximo de j, m.

O(1) ∗ (O(n + m) + ni=0 ( m (1 + m


P P P
Pn Pm i=0 i=0 (O(i + j)))))
O(1) ∗ (O(n + m) + i=0 ( i=0 (O(1 + nm + m2 ))))
O(1) ∗ (O(n + m) + ni=0 (O(m + nm2 + m3 )))
P

O(1) ∗ (O(n + m) + O(nm + (nm)2 + nm3 ))


O(1) ∗ (O(n + m + nm + (nm)2 + nm3 ))
O(n + m + nm + (nm)2 + nm3 )
O((nm)2 + nm3 )
Se, n > ou = m :
O((nm)2 ).
Se, m > n :
O(nm3 ).

4 Conclusão
O conceito de Matrizes Esparsas é muito útil quando se trata da representação de
poucos números em um grande bloco de números, mas a memória consumida no
caso de um bloco pequeno é muito maior do que é esperado, sendo melhor criar
matrizes não esparsas como a do TP1[1] . Durante o estruturamento da matriz,
a única diculdade foi a manipulação dos ponteiros, para que se encaixassem de
acordo com que a estrutura casse sempre circular. Depois da estrutura pronta,
montei a estrutura de imprimir e de somar sem problemas, capturando os itens
direto das matrizes, mas isso fazia as funções simples carem muito extensas, e
quando chegou a vez de implementar a função de multiplicação, que teria que
armazenar muitos valores para a composição de um valor na nova matriz, ai sim
existiu o grande problema, por o código caria realmente muito mais extenso, então
observando novamente o TP1, tive a idéia de criar a função get, que me retornava os
valores diretamente sem precisar deixar os códigos extensos como estavam. Então
simpliquei as funções de imprimir e somar com a nova get e z tranquilamente à
função de multiplicar.

12
Referências
[1]
http://www.republicaartemanha.com/Quase/TMatriz.pdf

Produzido em LATEX

13

Você também pode gostar