/// <summary> /// Overload utilizada por sua versão pública para encontrar recursivamente o melhor caminho /// </summary> /// <param name="cidadeOrigem">Índice da cidade de partida</param> /// <param name="cidadeDestino">Índice da cidade de destino</param> /// <returns>Pilha com todos os trajetos necessários para chegar de uma cidade a outra. Se retornar vazia, não existe caminho entre as duas cidades</returns> protected Stack <Movimento> RecursaoMelhorCaminho(int cidadeOrigem, int cidadeDestino) { double custo = double.MaxValue; Stack <Movimento> ret = new Stack <Movimento>(); vertices[cidadeOrigem].foiVisitado = true; // Evita que suas chamadas filhas voltem de cidade e entrem em loop // Caso haja conexão direta entre a cidade atual e a cidade de destino e esta conexão for melhor que o melhor caminho até agora, // a consideramos o melhor caminho if (adjMatrix[cidadeOrigem, cidadeDestino] != 0) { Movimento m = new Movimento(); m.setValores(cidadeOrigem, cidadeDestino); ret.Push(m); custo = adjMatrix[cidadeOrigem, cidadeDestino]; } for (int saidaAtual = 0; saidaAtual < numVerts; saidaAtual++) // Testa o melhor caminho por todas as saídas { if (adjMatrix[cidadeOrigem, saidaAtual] == 0) // Não há saída da cidade de origem pela saidaAtual { continue; } // Nunca passa duas vezes pelo mesmo local. Serve como condição de saída, já que, caso todas as saídas já tenham sido visitadas, // o método não se chama novamente. if (vertices[saidaAtual].foiVisitado) { continue; } if (saidaAtual == cidadeDestino) // Esta condição já fora tratada fora do for { continue; } Stack <Movimento> caminhoEncontrado = RecursaoMelhorCaminho(saidaAtual, cidadeDestino); if (caminhoEncontrado.Count <= 0) // Caso não encontrou caminho, a saída nem precisa ser avaliada { continue; } // Avalia a eficácia do caminho Stack <Movimento> aux = new Stack <Movimento>(); double custoAtual = 0; while (caminhoEncontrado.Count > 0) { Movimento m = caminhoEncontrado.Pop(); custoAtual += adjMatrix[m.getCidade(), m.getSaida()]; aux.Push(m); } custoAtual += adjMatrix[cidadeOrigem, saidaAtual]; // Se o caminho encontrado for o melhor até agora, o armazenamos if (custoAtual < custo) { ret.Clear(); // Adiciona o trajeto da cidade de origem ate a saída onde se inicia o caminho Movimento m = new Movimento(); m.setValores(cidadeOrigem, saidaAtual); ret.Push(m); while (aux.Count > 0) // Restaura o caminho encontrado, em ordem, na pilha de retorno { ret.Push(aux.Pop()); } custo = custoAtual; } } vertices[cidadeOrigem].foiVisitado = false; // Faz isso para permitir que outras chamadas (anteriores) utilizem aquele vértice return(ret); }
/// <summary> /// Busca o melhor caminho possível entre duas cidades usando o algorítmo iterativo de Dijkstra /// </summary> /// <param name="cidadeOrigem">Cidade de origem</param> /// <param name="cidadeDestino"> Cidade destino</param> /// <returns>Pilha com todos os trajetos necessários para chegar de uma cidade a outra. Se retornar vazia, não existe caminho entre as duas cidades</returns> public Stack <Movimento> DijkstraMelhorCaminho(string cidadeOrigem, string cidadeDestino) { Dictionary <int, int> trajeto = new Dictionary <int, int>(); // Cidade atual, cidade anterior int iCidadeDestino = IndiceDe(cidadeDestino); double[] distancias = new double[numVerts]; for (int i = 0; i < numVerts; i++) // Inicializa todas as distâncias com infinito { distancias[i] = double.MaxValue; vertices[i].foiVisitado = false; } int cidadeAtual = IndiceDe(cidadeOrigem); distancias[cidadeAtual] = 0; // A distância do início até ele mesmo é 0 for (int visitados = 0; visitados < numVerts; visitados++) // Visita todos os vértices { // Seleciona o próximo vértice a ser processado, pelo critério de menor distância int custoAtual = int.MaxValue; for (int i = 0; i < numVerts; i++) { if (vertices[i].foiVisitado) { continue; } if (distancias[i] < custoAtual) { cidadeAtual = i; } } vertices[cidadeAtual].foiVisitado = true; for (int saidaAtual = 0; saidaAtual < numVerts; saidaAtual++) // Percorre as conexões da cidade atual { if (adjMatrix[cidadeAtual, saidaAtual] == 0) // Caso não haja conexão entre as duas cidades { continue; } double dist = distancias[cidadeAtual] + adjMatrix[cidadeAtual, saidaAtual]; if (dist < distancias[saidaAtual]) // Se encontrar um caminho ainda mais curto do atual, atualiza no vetor { distancias[saidaAtual] = dist; trajeto[saidaAtual] = cidadeAtual; // A cidade anterior à saidaAtual é cidadeAtual } } } // Interpreta o vetor de trajeto Movimento m; Stack <Movimento> ret = new Stack <Movimento>(); cidadeAtual = iCidadeDestino; while (trajeto.ContainsKey(cidadeAtual)) { m = new Movimento(); m.setValores(trajeto[cidadeAtual], cidadeAtual); ret.Push(m); cidadeAtual = trajeto[cidadeAtual]; } return(new Stack <Movimento>(ret)); }
/// <summary> /// Busca o melhor caminho possível entre duas cidades usando Backtracking iterativo /// </summary> /// <param name="cidadeOrigem">Cidade de partida</param> /// <param name="cidadeDestino">Cidade destino</param> /// <returns>Pilha com todos os trajetos necessários para chegar de uma cidade a outra. Se retornar nula, não existe caminho entre as duas cidades</returns> public Stack <Movimento> BacktrackingMelhorCaminho(string cidadeOrigem, string cidadeDestino) { for (int i = 0; i < numVerts; i++) { vertices[i].foiVisitado = false; } Stack <Movimento> result = new Stack <Movimento>(); Stack <Movimento> p = new Stack <Movimento>(); bool achou = false; int cidadeAtual; int saidaAtual = 0; cidadeAtual = IndiceDe(cidadeOrigem); while (!(cidadeAtual == IndiceDe(cidadeOrigem) && saidaAtual == numVerts && p.Count <= 0)) { // só sai do while quando tiver tentado todos os caminhos while (saidaAtual < numVerts && !achou) { if (adjMatrix[cidadeAtual, saidaAtual] == 0) // se não houver conexão entre a cidadeAtual e a saidaAtual { saidaAtual++; // tenta a próxima saída } else if (vertices[saidaAtual].foiVisitado) // Se já tentamos a saidaAtual { saidaAtual++; // tenta a próxima saída } else if (saidaAtual == IndiceDe(cidadeDestino)) // Se chegamos aonde queríamos { Movimento movim = new Movimento(); movim.setValores(cidadeAtual, saidaAtual); p.Push(movim); achou = true; // achamos uma das possíveis rotas } else { Movimento movim = new Movimento(); movim.setValores(cidadeAtual, saidaAtual); p.Push(movim); // adicionamos o último movimento vertices[cidadeAtual].foiVisitado = true; cidadeAtual = saidaAtual; // vamos para a saidaAtual saidaAtual = 0; // procuramos novamente por novas saídas } } if (!achou) { if (p.Count > 0) { Movimento movim = (Movimento)p.Pop(); saidaAtual = movim.getSaida(); cidadeAtual = movim.getCidade(); movim = null; saidaAtual++; } } else // se achou um novo caminho, mede sua eficiência e se for melhor, o coloca na pilha de resultado { Stack <Movimento> aux = new Stack <Movimento>(); double distanciaI = 0; if (result.Count == 0) { distanciaI = double.MaxValue; } while (result.Count > 0) { Movimento mov = result.Pop(); distanciaI += adjMatrix[mov.getCidade(), mov.getSaida()]; aux.Push(mov); } while (aux.Count > 0) { result.Push(aux.Pop()); } double criterioNovo = 0; while (p.Count > 0) { Movimento mov = p.Pop(); criterioNovo += adjMatrix[mov.getCidade(), mov.getSaida()]; aux.Push(mov); } while (aux.Count > 0) { p.Push(aux.Pop()); } if (criterioNovo <= distanciaI) { aux = new Stack <Movimento>(); result = new Stack <Movimento>(); while (p.Count > 0) { aux.Push(p.Pop()); } while (aux.Count > 0) { Movimento mov = aux.Pop(); result.Push(mov); p.Push(mov); } } if (p.Count > 0) { Movimento movim = (Movimento)p.Pop(); saidaAtual = movim.getSaida(); cidadeAtual = movim.getCidade(); movim = null; saidaAtual++; } achou = false; } } return(result); }