Um grafo para teste (crédito: Gregor Ulm) |
Sem entrar em detalhes na teoria (se quiser, veja as apresentações no curso), um grafo é composto por vértices (ou nós), interligados por arestas. No caso em questão, as arestas são direcionais, isto é, podem ser seguidos apenas em uma direção. O problema em estudo é encontrar no grafo conjuntos de nós fortemente conectados (isto é, de qualquer nó do conjunto é possível chegar a qualquer outro). A figura no início (feita por Gregor Ulm como caso de teste) resume visualmente o problema (cada cor indica um conjunto fortemente conectado).
Tradicionalmente, se usa n para indicar o número de nós e m o número de arestas. O curso apresenta o algorítimo de Kosaraju, que tem o potencial de ser extremamente rápido, com tempo proporcional à soma (n + m). E precisa mesmo ser rápido, pois o grafo a ser analisado tem quase 9000 mil nós e um pouco mais de 5 milhões de arestas. O algorítimo envolve analisar recursivamente todas as arestas que saem de cada nó (novamente, detalhes no material do curso). O arquivo de entrada possui uma linha para cada aresta, contendo o número do nó origem e do nó destino.
Comecei fazendo uma implementação direta do algorítimo (até agora estou resolvendo os problemas com C puro, sem nenhuma biblioteca especial). A estrutura para para representar o grafo foi uma imagem direta da entrada: um vetor com cada posição contendo a origem e destino de uma aresta. Após alguma briga, o programa processou com sucesso os casos de testes. Não conseguiu, entretanto, processar o grafo do exercício.
Lista de arestas do grafo |
O processamento continuou muito lento. Em umas "contas de padaria" estimei umas 2 ou 3 horas de processamento no meu fiel Pentinum 4 2.8 HT. Foi quando me dei conta da bobagem que eu estava fazendo. Para processar as arestas de um nó eu examinava todas as arestas, portanto o meu processamento era proporcional a (n * m), não a (n + m).
Hora de mudar de estrutura de dados para representar o grafo. A nova estrutura consiste em um vetor onde os índices são os nós de origem e cada elemento é uma lista ligada de nós destino. Este mexida foi mais trabalhosa que a anterior, mas deu menos trabalho para debugar do que eu esperava.
Mesmo grafo, outra estrutura |
Obs: Além do exercício de programação, toda semana tem também uma provinha. Normalmente eu faço primeiro a prova e depois o exercício, mas desta vez resolvi fazer o contrário. Qual não foi minha surpresa ao ver que uma das questões era sobre a desvantagem de usar uma outra representação para o grafo. Se eu tivesse feito a prova primeiro, provavelmente teria percebido que ia usar uma estrutura inadequada.
Nenhum comentário:
Postar um comentário