A aplicação do algoritmo de menor caminho na analise de um labirinto
1. Introdução
2. Desenvolvimento Desenvolvimento A origem da teoria dos grafos é, em geral, associada ao problema das pontes de Königsberg (cidade da Prússia que agora se designa por Kaliningrad). Parte desta cidade localizava-se em duas ilhas do rio Pregel as quais estavam ligadas às [1]:
margens e uma à outra através de 7 pontes, conforme a Figura 1 abaixo
Consta que os habitantes de Königsberg gostavam de dar passeios de modo a atravessar todas as pontes e que alguns andavam particularmente aborrecidos pelo fato de não encontrarem um trajeto (com partida e chegada a um mesmo lugar) que lhes permitisse atravessar apenas uma vez cada uma das pontes. O matemático suíço Leonhard Euler (1707-1783) ao tomar conhecimento deste problema resolveu-o (indicando a impossibilidade da existência de um tal percurso, numa memória que publicou em S. Petersburgo em 1736) modelando-o pelo multigrafo representado na Figura 2[1].
Segundo Rosen, 2009, grafos são estruturas que consistem em arestas e vértices que sem ligam entre si. Alem disso temos que um grafo é um conjunto (V,A) onde V é um conjunto arbitrário e A é um subconjunto de V. Os elementos de V são chamados vértices e os de A arestas. Quando um grafo não pode ter duas arestas paralelas e laços dizemos que ele é simples[4]. Grafos são amplamente utilizados na computação para auxiliar na resolução de diversos problemas com o auxilio de algoritmos modelados a partir desses. Podemos citar como exemplo os seguintes problemas: problema do caminho mínimo entre dois nós, problema do caixeiro viajante, problema da arvore espalhada mínima e a coloração dos mapas, onde verificou-se que quatro cores seriam o numero mínimo para pintar um mapa sem que países vizinhos tivessem as mesmas cores. No entanto, para se chegar até
a solução é necessário realizar uma pesquisa
sistemática das arestas de modo a lançar o vértice e como descreveremos a seguir este pode ser feito através de duas técnicas para percorrer o grafo. Porém precisamos primeiro conceituar o caminho no grafo. Para Carmo, Netto e Portugal, 2002, um caminho é qualquer grafo ou subgrafo isomorfo a (V,E) dado por
{ } para algum k pertencente N. Em um caminho P dizemos que os vértices de grau 1 são os extremos de P e os de grau 2 são os internos de P. Já o numero de arestas representa o comprimento do caminho e a distancia entre dois vértices quaisquer u, v e V é definida como o menor caminho com extremos u, v. No entanto, para realizar a busca no menor caminho em um labirinto qualquer necessitamos de dois conceitos básicos: busca em profundidade e largura.
2.1 Busca em profundidade Dado um grafo orientado G = (V; E). Onde V é o conjunto de vértices e E o conjunto de arestas ligando os vértices. A estratégia utilizada no algoritmo de busca em profundidade consiste em percorrer o grafo, passando por todos os vértices v que ainda tem arestas inexploradas. Quando todos os vértices de um caminho são explorados a busca regressa para explorar outras arestas que deixam o vértice a partir do qual v foi descoberto [3].
Como o próprio nome diz, ele se caracteriza por começar num nó raiz (selecionando algum nó como sendo a raiz, no caso de um grafo) e explora tanto quanto possível cada um dos seus ramos, antes de retroceder.
2.2 Busca em Largura O algoritmo de busca em largura é utilizado para percorrer grafos, explorando sistematicamente arestas vizinhas a partir de um no origem s, expandindo a fronteira de vértices descobertos uniformemente e explorando a cada iteração os nós vizinhos não visitados. O algoritmo consegue explorar todo o grafo, descobrindo vértices e as distâncias para o vértice origem quando o grafo esta representado em lista de adjacências em O(|E| + |V|) [3]. Sendo assim podemos perceber que o algoritmo de busca em largura funciona da seguinte forma: começa pelo nó raiz e explora todos os nós vizinhos. Então, para cada um desses nós mais próximos, explora os seus nós vizinhos inexplorados e assim por diante, até que ele encontre o alvo da busca. O algoritmo utiliza, portanto, uma fila (FIFO - First In First Out ). Em implementações típicas, nós que ainda não foram examinados por seus vizinhos são colocados na lista de adjacência.
2.3 Materiais e Métodos Para realizar a experiência de busca do menor caminho em um labirinto foram realizados os seguintes passos: desenhar um labirinto no software Gimp sendo que os caminhos possíveis caracterizaram-se pela cor branca, o inicio pela cor azul, o fim pela cor vermelha e o restante da imagem pela cor preta. As cores utilizadas foram cores puras utilizando-se do valor da cor com base no principio R,G,B. Para continuar a experiência lançou-se mão da IDE Netbeans para realizar a programação, onde baseou-se no algoritmo de largura adaptado para alcançar o objetivo desse artigo.
2.4 Resultados O algoritmo de busca em largura é uma parte essencial para chegar aos objetivos da experiência. O pseudocódigo abaixo demonstra seu funcionamento: 1 BFS (G, V, E, s) 2 3
para cada vértice v em V cor[v] ← branco
4 cor[s] cinza ←branco 5
inicializar fila q
No entanto, para finalizar a experiência que seu resume a encontrar o menor caminho até a saida de um labirinto qualquer necessitou-se fazer alterações no algoritmo de busca em largura onde adicionou-se as linhas 7 e 14: 1 BFS (G, V, E, s) 2 3 4
para cada vértice v em V cor[v] ← branco cor[s] ← cinza
5 inicializar fila q 6 adiciona_fila(q,s) 7 anterior[s] ← NULO
8
enquanto fila_nao_vazia(q) 9
vert ← proximo_fila(q)
10
cor[v] ← preto
11
para cada vértice viz em lista_vizinho(vert) 12
se cor[viz] = branco
13
cor[viz] ← cinza
14 anterior[viz]
15
← vert
adiciona_fila(q,viz)
Temos, então,a seguinte imagem como entrada do algoritmo:
Para realizar a busca dos vértices no arquivo bitmap e adicioná-los em uma lista de adjacência foi implementado o seguinte código: 1 void busca_vertices_do_grafo() { 2 g.lista_vertices = criar_lista(); 3 for (int y = 0; y < altura; y++) { 4 5
for (int x = 0; x < largura; x++) { if (is_blue(x, y) || is_red(x, y) || is_white(x, y)) {
11
p->y = y;
12
v->pixel = p;
13
inserir_final_lista(g.lista_vertices, v);
14
if (is_blue(x, y)) {
15
inicio = v;}
16
if (is_red(x, y)) {
17
fim = v;}
Esse trecho de código percorre os pixels e cria uma lista para adicioná-los conforme sua cor e sua vizinhança. Caso o pixel seja vermelho ele é adicionado no fim da lista e caso seja azul é adicionado ao inicio da lista, sendo assim verifica-se que ele atende a definição de busca em largura. Além disso, para criar a lista de adjacência e atender toda a definição é criado a função executar_funcao_lista que tem como parâmetro uma lista que foi definida e preenchida na função anterior quando o algoritmo percorre a imagem para perceber quais is pixels existentes e seus vizinhos. 1 nodo *iterador = l->inicio; 2
while (iterador != NULL3
Tendofunc(iterador->dado); executado esse processo podemos chegar ao algoritmo de busca em 3 largura4 em siiterador o qual= iterador->prox; irá encontrar o menor caminho a partir do grafo e da lista de adjacência, temos: 1
void menor_caminho(grafo g, vértice *inicio) {
2
inicio->cor = CINZA;
3 4
lista *fila = criar_lista(); inserir_final_lista(fila, inicio);
5 6 7
while (!lista_vazia(fila)) { vértice *v = (vértice*) remover_inicio_lista(fila); v->cor = PRETO;
8 9
pixel *p = v->pixel; lista *adjacentes = v->lista_adjacencia;
10 11 12 13 14 15 16 17 18
nodo* iterador = adjacentes->inicio; while (iterador != NULL) { vértice *viz = ((vértice*) (iterador->dado)); if (viz->cor == BRANCO) { viz->cor = CINZA; viz->anterior=v; inserir_final_lista(fila,viz);} iterador = iterador->prox; }
Após colorir de preto os vizinhos mais próximos do grafo ele então executa a função pinta_pixels que irá colorir de vermelho na imagem o menor caminho encontrado até a saída do labirinto: 1 2 3 4 5
vértice* iterador = fim; while(iterador != inicio){ set_red(iterador->pixel->x,iterador->pixel->y); iterador=iterador->anterior;} save_as("saida.bmp");}
Sendo assim obtemos o seguinte resultado para a imagem inicial:
3. Conclusão
4. Referencias 1. CARDOSO, D. M. Teoria dos Grafos e Aplicações. Departamento de Matemática da Universidade de Aveiro, 2004. Disponível em Acesso em 6 de abril de 2012
2. Carmo, M. R. R. do; Netto, P. O. B.; Portugal, L. da S. Uma heurística interativa para geração de caminhos em grafos com restrição de grau: aplicação ao projeto de sistemas metroviários. Rio de Janeiro: Pesquisa Operacional, v. 22 nº 1, 2002. Disponível em: < http://www.scielo.br/pdf/pope/v22n1/a02v22n1.pdf > Acesso em 25/08/2013 3 . Cormen, T. H.; Leiserson C. E.; Rivest, R. L.; Stein, C. Introduction to Algorithms, The MIT Press e McGraw-Hill, 2009.Disponível em:< http://en.tjcities.com/wp-content/uploads/Books/Algorithms_3rd.pdf > Acesso em 25/08/2013
4. Feofiloff , P.; Kohayakawa, Y.; Wakabayashi, Y. Teoria dos Grafos. São Paulo: Instituto de Matemática e Estatística, USP. 2011. Disponível em: Acesso em 25/08/2013 5. ROSEN, K. H. Matemática Discreta e suas Aplicações. São Paulo: McGraw-Hill, 2009. Pg 589 – 675.