Distâncias entre vértces em um grafo. Implementação sequencial

Documentos relacionados
Distâncias entre vértces em um grafo. Paralelização. André de Freitas Smaira

Universidade de São Paulo - USP. Instituto de Física de São Carlos - IFSC. Programação de Alto Desempenho. Prática 1. André de Freitas Smaira

Acessibilidade e Fecho Transitivo de Grafos Dirigidos

Facebook. Um grafo é uma rede. Estrutura de dados fundamental em Informática, tal como listas e árvores.

Instituto de Matemática e Estatística - USP MAC Organização de Computadores EP1. Experimentos com o cache. Tiago Andrade Togores

CIC 110 Análise e Projeto de Algoritmos I

Algoritmo de Dijkstra Estudo e Implementação

Otimização em Grafos

Pesquisa Linear. Adriano J. Holanda 15/3/2016

SCC Laboratório de Algoritmos Avançados. Multigrafos: Caminhos Mínimos. Multigrafos e Multidígrafos. Multigrafos e Multidígrafos

Grafos - Representação

QUESTÕES DE PROVAS ANTIGAS

Prova Didática Grafos: Árvores Geradoras e Caminhos Mínimos, Análise de Complexidade

Estrutura de Dados e Algoritmos e Programação e Computadores II. Aula 4: Listas Estáticas e Dinâmicas

Representações de Grafos

Complexidade de algoritmos Notação Big-O

Aula 09. Percurso em grafo

CRIVO QUADRÁTICO: IMPLEMENTAÇÃO DA OBTENÇÃO DE UM CONJUNTO DE NÚMEROS COMPLETAMENTE FATORADOS SOBRE UMA BASE DE FATORES

Teoria dos Grafos Aula 3

Algoritmos em Grafos: Caminho Mínimo

Análise empírica de algoritmos de ordenação

4 Testes e experimentos realizados 4.1. Implementação e banco de dados

Grafos tipo abstrato de dados

Listas Estáticas. SCC Algoritmos e Estruturas de Dados I. Prof. Fernando V. Paulovich. *Baseado no material do Prof.

Distâncias Mínimas. Pedro Ribeiro 2014/2015 DCC/FCUP. Pedro Ribeiro (DCC/FCUP) Distâncias Mínimas 2014/ / 27

A IMPORTÂNCIA DE THREADS NO DESEMPENHO DE APLICAÇÕES

Equação de Poisson. Paulo Matias. f (x, y) =

Grafos. Notas. Notas. Notas. Notas. Caminhos mais curtos de todos os pares

SCC0503 (Algoritmos e Estruturas de Dados II) Prof. Moacir P. Ponti Junior. Trabalho 2

Árvore Geradora Mínima

6 Resultados Análise de Desempenho

Teoria dos Grafos Aula 6

Melhores momentos AULA 4

Grafos - Introdução. Pedro Ribeiro 2014/2015 DCC/FCUP. Pedro Ribeiro (DCC/FCUP) Grafos - Introdução 2014/ / 32

Um algoritmo pseudo-periférico genérico para a heurística de Snay

Algoritmo de Dijkstra (um para todos ; arestas de peso não negativo ; guloso)

Análise de Algoritmos

1. Selecione a Estrutura de Dados que melhor representa os diretórios ou pastas de arquivos do computador.

Algoritmo de Dijkstra Wikipédia, a enciclopédia livre

Medida do Tempo de Execução de um Programa. David Menotti Algoritmos e Estruturas de Dados II DInf UFPR

Problema do Caminho Mínimo

6 Experimentos realizados

06 Grafos: Caminhos Mínimos SCC0503 Algoritmos e Estruturas de Dados II

5 Qualidade dos Resultados

Estruturas de Dados Estruturas de Dados Fundamentais

ALOCAÇÃO DINÂMICA DE MEMÓRIA

Análise de Desempenho de Estratégias de Particionamento de Grafos

Teoria dos Grafos Aula 23

Calculando distâncias

UNIVERSIDADE DE SÃO PAULO INSTITUTO DE CIÊNCIAS MATEMÁTICAS E DE COMPUTAÇÃO Departamento de Ciências de Computação

Aula 08. Estruturas de dados Árvore e Grafo

AULA 11 PROJETO E ANÁLISE DE ALGORITMOS. Conceitos básicos e representação de grafos Karina Valdivia Delgado

Filas. Nesta aula veremos o ADT fila Em um computador existem muitas filas esperando pela impressora, acesso ao disco ou, num sistema timesharing,

UNIVERSIDADE FEDERAL FLUMINENSE INSTITUTO DE COMPUTAÇÃO DEPARTAMENTO DE CIÊNCIA DA COMPUTAÇÃO

Questão 1: O histograma deve ser: [0, 1, 4, 2, 0, 3, 0,, 0, 2] Exemplo: Para a matriz

Listas Estáticas. Prof. Fernando V. Paulovich *Baseado no material do Prof. Gustavo Batista

Quantidade de memória necessária

Algoritmos e Estrutura de Dados II. Árvore. Prof a Karina Oliveira.

Grafos: Busca. Algoritmos e Estruturas de Dados 2. Graça Nunes

Projeto e Análise de Algoritmos

AED2 - Aula 22 Busca em largura, cálculo de distâncias

MAC328 Algoritmos em grafos (4/6/2004) Caminhos mínimos

Medida do Tempo de Execução de um Programa. Bruno Hott Algoritmos e Estruturas de Dados I DECSI UFOP

Árvores Binárias. SCC Algoritmos e Estruturas de Dados I. Prof. Fernando V. Paulovich

grafo nós vértices arcos arestas

Edital de Seleção 053/2016 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

Teoria dos Grafos Aula 22

Na última aula... Procurando um caminho. Certificados. Procurando um caminho

Teoria dos Grafos Aula 8

AED Algoritmos e Estruturas de Dados LEEC /2007. Teoria de Grafos e Algoritmos em Grafos

AULA 4. Procurando um caminho. Melhores momentos. Certicados. Certicado de inexistência

INF 1010 Estruturas de Dados Avançadas

4 Resultados. 4.1 Análise de desempenho

Aula T19 BCC202 Pesquisa (Parte 1) Pesquisa Binária. Túlio Toffolo

Desafios de Programação TCC Turma A-1

Grafos Caminhos mais Curtos

Caminhos mínimos de todos os pares

Na última aula... Algoritmos em Grafos 1º sem / 80

IMPLEMENTAÇÕES PARALELAS PARA FECHO TRANSITIVO

Vamos considerar um arquivo de dados que armazena uma lista de alunos. Cada registro é um objeto com um número de matrícula e um nome.

Filas de prioridade. Marcelo K. Albertini. 27 de Novembro de 2014

ALGORITMOS E ESTRUTURAS DE DADOS 2011/2012 ANÁLISE DE ALGORITMOS. Armanda Rodrigues 3 de Outubro 2011

4 Velvet 4.1. Estrutura VelvetH VelvetG

Algoritmos e Estruturas de Dados 2005/2006. Algoritmo: conjunto claramente especificado de instruções a seguir para resolver um problema

Gerência de Memória. Gerência de Memória Introdução e Particionamento. Novo capítulo. Aulas anteriores. Plano da aula. Memória lógica & física

Melhores momentos AULAS 1-8

Grafos: caminhos mínimos

AULA 14 ESTRUTURA DE DADOS

BCC202 - Estrutura de Dados I

Projeto e Análise de Algoritmos

Algoritmo Floyd-Warshall. Problema dos caminhos mínimos entre todos os pares. Programação dinâmica

5.1. Fluxo para geração do Roadmap

Tabelas Hash. informação, a partir do conhecimento de sua chave. Hashing é uma maneira de organizar dados que:

03 Grafos: percurso, ponderação e caminhos SCC0503 Algoritmos e Estruturas de Dados II

3 Montagem de Fragmentos

Grafos - Representação

Transcrição:

Distâncias entre vértces em um grafo Implementação sequencial André de Freitas Smaira 16 de outubro de 2013 1

1 Introdução Nesse projeto, temos por objetivo a determinação das distâncias mínimas entre todos os pares existentes de vértices em um grafo suficientemente grande para que sua representação em uma matriz de adjacência comum (formada por array 2D) seja inviável. Para tal, temos que utilizar estruturas diferentes, que ocupem somente o espaço necessário, isto é, somente seja alocado espaço para as arestas existentes. Além disso temos que escolher o algoritmo que mais se adeque à situação, tanto em relação à quantidade de dados armazenados durante o processo, quanto ao desempenho do programa e à facilidade de se realizar posteriores paralelizações. 2 Algoritmo Para essa situação, instantaneamente pensamos em dois algoritmos. São eles: Floyd-Warshall: O [N 3 ] Dijkstra: O [NE + N 2 log (N)] sendo N o número de vértices e E o número de arestas. Vejamos agora qual escolher. O algoritmo de Floyd-Warshall (O [N 3 ]) testa para cada possível intermediário, todos os pares de vértices de forma a minimizar o caminho entre eles, guardando todos as distâncias entre todos os pares a cada iteração. Se o grafo for conexo, ao fim do processo, teremos uma matriz quadrada N N armazenada com todas as mínimas distâncias, o que é inviável para a nossa situação. Além disso, a complexidade do algoritmo é, na maioria das vezes, bem superior à do algoritmo de Dijkstra. O algoritmo de Dijkstra (O [E + N log (N)]) calcula as distâncias de um vértice inicial até todos os outros através de um algoritmo guloso que se assemelha ao BFS, armazenando somente um vetor de distâncias de tamanho N, um vetor de vértices visitados de tamanho N e uma fila de prioridades de tamanho máximo E durante o processo. Repetindo isso para cada vértice inicial, obtemos todas as distâncias entre cada par de vértices a partir de um algoritmo O [NE + N 2 log (N)], portanto mais eficiente que o anterior para um grafo de baixa densidade 1

( ) E N < N. Além disso, intuitivamente temos uma distribuição de paralelização bem simples, entregando um vértice inicial para cada processo, totalizando N processos independentes entre si. 3 Representação dos Dados Para representarmos esse grafo não podemos usar uma matriz de adjacência composta por um array 2D estático tradicional de C. Temos que usar uma representação que inicia vazia e que aumenta com o acréscimo de arestas. Para representarmos os nós, podemos simplesmente usar inteiros de 0 a N 1 como representado na entrada do programa. Para ligarmos esses nós (arestas), uma opção (a utilizada por mim) é utilizarmos um a matriz de adjacência, porém, ao contrário do comumente usado, dinâmica, em que adicionamos arestas (edge, como mostrado abaixo) apenas se ela existir no grafo. typedef struct { } edge ; int d e s t ; // v e r t i c e d e s t i n o f l o a t w; // d i s t a n c i a e n t r e v e r t i c e s Para a representação da linha i da matriz de adjacência, isto é, as arestas que saem do vértice i, podemos usar a seguinte estrutura: typedef struct { } l i n e ; std : : l i s t <edge> l ; Finalmente para a representação da matriz como um todo, utilizamos um array 1D de N elementos do tipo line definido acima. Juntando a matriz de adjacência, o número de vértices e o número de arestas, pode-se criar a classe graph, com o seguinte protótipo: 2

class graph { int _nv, _na ; // nodes and edges number l i n e _adj ; // a d j a c e n t matrix public : graph ( int nv, int na ) : _nv( nv ), _na( na ) { _adj = new l i n e [ nv ] ; } // c r e a t e t h e graph graph ( graph const& g ) ; // copy c o n s t r u c t o r graph ( ) { delete [ ] _adj ; } // graph d e s t r o y e r void i n s e r t ( int o r i g, int dest, f l o a t w) ; // i n s e r t t h e edge from o r i g to d e s t with c o s t w bool s e t ( int o r i g, int dest, f l o a t w) ; // s e t t h e edge from o r i g to d e s t with c o s t w. r e t u r n f a l s e i f t h e edge doesn t e x i s t f l o a t d i s t ( int o r i g, int d e s t ) ; // d i s t a n c e from o r i g to d e s t. r e t u r n 1, i f t h e path doesn t a l r e a d y e x i s t int s i z e ( ) { return _na ; } // edges number int o r d e r ( ) { return _nv ; } // nodes number void p r i n t ( ) ; // p r i n t t h e a d j a c e n t matrix on t h e standard output (DEBUG) } ; Esse modelo de representação foi considerado adequado pelo pequeno gasto de memória adicionado à facilidade de implementação, já que a estrutura utilizada é basicamente uma matriz normal, mas com a maioria dos elementos faltantes no caso de uma matriz esparsa, como é a proposta deste trabalho. Outra coisa importante a se destacar é minha decisão em utilizar uma função externa à classe graph para calcular as distâncias mínimas. Fiz isso por uma questão simplesmente de estética, pois, apesar de gastar muito mais tempo, esse não é um método intrínseco ao grafo, isto é, não é um método que alterasse ou retornasse uma característica do grafo de forma simples 3

e sim uma rotina mais complexa que se utiliza de métodos da classe para obter os resultados desejados. Perceba que essa decisão só altera a constante de desempenho do algoritmo e não a complexidade do mesmo. Como o objetivo desses trabalhos é a comparação do algoritmo nas implementações sequencial e paralelas, considero essa uma decisão válida. Se essa diferença de tempo fosse um prejuízo importante para o objetivo do projeto, com certeza essa rotina deveria ser um método da classe. 4 Avaliação de Desempenho Para avaliação do desempenho, foi utilizada a função local_time() da biblioteca posix_time.hpp para medir o tempo de execução somente do algoritmo. Essa informação foi exibida pelo programa na saída padrão no formato n o de vértices n o de arestas tempo de execução Salvando os dados de várias execuções em um arquivo, obtemos gráficos para a análise de desempenho do programa. Vamos começar por fixar o número de vértices e análisar os resultados, com o objetivo de observar a complexidade do algoritmo já citada. Compilamos o programa descrito (seq.cpp) com a flag de otimização O2: g++ O2 seq. cpp o seq Com essa linha de compilação e executando o programa, para todos os casos de teste disponibilizados e vários outros gerados, num computador com as características descritas na seção 5, obtemos, sem nenhum erro nos resultados, o arquivo com os tempos. 4.1 Variando o número de arestas Primeiramente, vamos fixar o número de vértices em valores diferentes e observar as semelhanças e diferenças entre eles. Sabemos, devido à complexidade do algoritmo [1], que o tempo de execução é: t (N, E) = k 1 NE + k 2 N 2 log (N) 4

Fixando N, temos: t (E) = C 1 E + C 2 isto é, uma equação linear com coeficiente angular C 1 = k 1 N e coeficiente linear C 2 = k 2 N 2 log (N). Portanto tentaremos ajustar uma reta para essa primeira forma de análise para cada um dos casos e esperamos, daí, obter as constantes de proporcionalidade do algoritmo. Começaremos por um valor pequeno (N = 60). Abaixo, vemos gráfico relativo a esse valor de N. x 10 3 60 vértices 7 6 Tempo de Execução, t (s) 5 4 3 2 1 0 0 100 200 300 400 500 600 Número de Arestas N o arestas Tempo de execução (s) 60 0, 000620 240 0, 001943 420 0, 003009 600 0, 004953 Observamos um comportamento linear como previsto anteriormente. A partir desses resultados e de um ajuste dos pontos em uma reta, podemos obter as constandes da complexidade desse algoritmo: C 1 = 7, 814 10 6 C 2 = 5, 267 10 5 k 2 = k 1 = C 1 N = 1, 3023 10 7 C 2 N 2 log (N) = 3, 5734 10 9 Vamos seguir para um valor de N uma ordem de grandeza acima (N = 660): 5

660 vértices 3.5 3 Tempo de Execução, t (s) 2.5 2 1.5 1 0.5 0 0 1000 2000 3000 4000 5000 6000 7000 Número de Arestas N o arestas Tempo de execução (s) 660 0, 042093 2640 1, 719374 4620 2, 169467 6600 2, 573474 Observe que no gráfico acima os pontos não são mais ajustados perfeitamente por uma reta. Por que isso ocorre se o algoritmo é o mesmo? A resposta a essa pergunta está relacionada à memória cache. O grande aumento de arestas faz com que a quantidade de cache misses aumente consideravelmente, deslocando os três últimos pontos para cima. Ajustando uma reta a todos esses pontos, como mostrado no gráfico, e adotanto a notação já descrita, temos: C 1 = 0, 0004063 C 2 = 0, 1513 k 2 = k 1 = C 1 N = 6, 1561 10 7 C 2 N 2 log (N) = 5, 3500 10 8 Observe uma ordem de grandeza maior, como esperado pelo aumento considerável de cache misses devido ao grande aumento de arestas do primeiro para o segundo ponto. Vamos agora fazer o mesmo ajustando somente os últimos três pontos: 6

660 vértices 3.5 3 Tempo de Execução 2.5 2 1.5 1 0.5 0 1000 2000 3000 4000 5000 6000 7000 Número de Arestas C 1 = 0, 0002157 C 2 = 1, 158 k 2 = k 1 = C 1 N = 3, 2682 10 7 C 2 N 2 log (N) = 4, 0947 10 7 Observe uma constante relacionada ao termo de primeiro grau bem mais próxima da primeira, pois os pontos tem apenas um pequeno aumento relativo de cache misses. A maior diferença está no coeficiente linear, pois o aumento conjunto dos 3 pontos é muito maior que o aumento relativo entre eles. Vamos agora fazer a análise para N = 1000. Nesse caso temos muito mais pontos, gerando um resultado bom, porém comparável apenas com os três últimos pontos do gráfico anterior por causa da grande deslocalização espacial. Vamos ao gráfico: 20 1000 vértices 18 16 14 Tempo de Execução, t (s) 12 10 8 6 4 2 0 0 0.2 0.4 0.6 0.8 1 Número de Arestas 1.2 1.4 1.6 1.8 2 x 10 4 7

C 1 = 0, 0005761 C 2 = 3, 066 k 2 = k 1 = C 1 N = 5, 7610 10 7 C 2 N 2 log (N) = 4, 4385 10 7 Observe que as duas constantes estão bem próximas das constantes obtidas para os três últimos pontos do gráfico anterior, como previsto, já que nos dois casos só se esgota o cache L1 de dados, como descrito na seção 5. 4.2 Variando ambos os parâmetros Podemos agora analisar o comportamento da variação tanto do número de vértices como do número de arestas ao mesmo tempo e novamente calcular as constantes da complexidade para comparar com os casos já obtidos. Para esse gráfico temos a seguinte equação: t (N, E) = k 1 NE + k 2 N 2 log (N) k 1 = 5, 744 10 6 k 2 = 9, 907 10 5 8

Nesse gráfico, observamos constantes bem maiores. Isso ocorre pois nele foi levado em conta todos os pontos (10 N 5000, 10 E 35000), o que causa grande variação de deslocalização espacial causando, para os valores de E mais altos, grande taxa de cache misses mesmo para o cache L2 (conforme descrito na seção 5), aumentando ainda mais as constantes de complexidade em relação às previamente encontradas. Uma evidência disso é que para a maioria dos pontos com N pequeno, temos um bom ajuste sobre a superfície, enquanto que para a maioria dos pontos com N alto, temos um ajuste muito ruim pela mesma superfície. 4.3 Variando a densidade Vamos agora ver o comportamento do tempo de execução em função da densidade (número de arestas para cada vértice). Os gráficos abaixo mostram esse comportamento. 900 800 700 600 Tempo de Execução, t (s) 500 400 300 200 100 0 0 2 4 6 8 10 12 14 16 18 Densidade 9

Observe nos gráficos, que a influência da densidade é pequena em relação ao número de vértices. Isso porque a complexidade do algoritmo varia linearmente com a densidade (como pode ser observado para os pontos mais baixos) e é quadrático com o número de vértices. Mas observe também um aumento repentino de tempo de execução em função da densidade quando ela passa de 1 para 2. Isso ocorre pois esse é o limite para o qual a maioria dos pontos ultrapassa o limite de tamanho que causa muitos cache misses e que tem muita deslocalização espacial, o que acarreta em um grande gradiente positivo na superfície do gráfico. 5 Conclusões Depois de toda a análise realisada, podemos concluir que a deslocalização espacial pode causar um grande prejuízo no desempenho de um programa e, portanto, devemos analisar bem as estruturas que serão empregadas em um programa para que isso ocorra a menor quantidade de vezes possível, mesmo em um programa com grande quantidade de dados como é o caso da situação analisada nesse trabalho. 6 Especificações do Computador Para essa prática, foi utilizado um notebook da marca Toshiba com o sistema operacional Ubuntu 13.04, com as seguintes configurações: Processador Memória RAM Memória Cache Intel R Core TM i5-2410m CPU 64-bit @ 2,30GHz 2,30GHz 6,00 GB L1 data: 2 32KB L1 instructions: 2 32KB L2: 2 256KB L3: 3MB 10

Referências [1] Algoritmo de Dijkstra - Wikipédia Disponível em: http://pt.wikipedia.org/wiki/algoritmo_de_dijkstra Acessado em: 13/10/2013 [2] Algoritmo de Floyd-Warshall - Wikipédia Disponível em: http://pt.wikipedia.org/wiki/algoritmo_de_floyd-warshall Acessado em: 13/10/2013 11