Quais algoritmos calculam as direções do ponto A ao ponto B em um mapa?

543

Como os fornecedores de mapas (como Google ou Yahoo! Maps) sugerem instruções?

Quero dizer, eles provavelmente têm dados do mundo real de alguma forma, certamente incluindo distâncias, mas também coisas como velocidades de condução, presença de calçadas, horários de trens etc. Mas suponha que os dados estejam em um formato mais simples, digamos um gráfico direcionado muito grande com pesos nas arestas refletindo distâncias. Quero poder calcular rapidamente direções de um ponto arbitrário para outro. Às vezes, esses pontos ficam próximos (dentro de uma cidade), enquanto às vezes ficam distantes (cross-country).

Algoritmos de gráficos como o algoritmo de Dijkstra não funcionarão porque o gráfico é enorme. Felizmente, algoritmos heurísticos como A * provavelmente funcionarão. No entanto, nossos dados são muito estruturados e talvez algum tipo de abordagem em camadas possa funcionar? (Por exemplo, armazene direções pré-computadas entre determinados pontos "principais", bem como algumas direções locais. Em seguida, as direções para dois pontos distantes envolverão direções locais para um ponto principal, direções globais para outro ponto principal e, em seguida, local instruções novamente.)

Quais algoritmos são realmente usados ​​na prática?

PS. Essa pergunta foi motivada por encontrar peculiaridades nas direções do mapeamento on-line. Ao contrário da desigualdade do triângulo, às vezes o Google Maps acha que o XZ leva mais tempo e é mais longe do que usar um ponto intermediário como no XYZ . Mas talvez as direções a pé também sejam otimizadas para outro parâmetro?

PPS. Aqui está outra violação da desigualdade do triângulo que sugere (para mim) que eles usem algum tipo de abordagem em camadas: XZ versus XYZ . O primeiro parece usar o famoso Boulevard de Sebastopol, embora esteja um pouco fora do caminho.

Editar : nenhum desses exemplos parece funcionar mais, mas ambos funcionavam no momento da postagem original.

A. Rex
fonte
3
BTW, O algoritmo A * "é uma generalização do algoritmo de Dijkstra que reduz o tamanho do subgrafo que deve ser explorado, se houver informações adicionais disponíveis que forneçam um limite inferior à" distância "do alvo"
Mitch Wheat
Re A *: sim, de fato. Felizmente, no nosso caso, essas "informações adicionais" estão disponíveis, por exemplo, usando a distância em linha reta. Quando digo "Dijkstra" acima, quero dizer baunilha Dijkstra.
A.Rex
Direções a pé? Não sei em nenhum outro lugar, mas por aqui (Hampshire, Reino Unido), o G grande não tem dados sobre pedestres - ele me direciona pelas estradas em torno dos recintos para pedestres etc.
jTresidder
Não me importo particularmente se as instruções são para dirigir ou caminhar. Eu só quero saber como eles funcionam! A razão pela qual tenho links para caminhadas é porque eu estava pensando em uma maneira de caminhar por Paris e ver todas as 66 fontes Wallace. (Os parâmetros desses mapas devem ser fontes de Wallace.)
A. Rex
A recompensa dessa pergunta é incentivar mais e melhores respostas, principalmente de pessoas que trabalham em um dos principais fornecedores. Comentários sobre estruturas de dados, algoritmos, quanto é pré-computado etc. são todos apreciados.
1955 Rex A. Rex

Respostas:

526

Falando como alguém que passou 18 meses trabalhando em uma empresa de mapeamento, que incluiu o trabalho no algoritmo de roteamento ... sim, o Dijkstra's funciona, com algumas modificações:

  • Em vez de fazer Dijkstra's uma vez da origem ao destino, você começa em cada extremidade e expande os dois lados até que eles se encontrem no meio. Isso elimina aproximadamente metade do trabalho (2 * pi * (r / 2) ^ 2 vs pi * r ^ 2).
  • Para evitar explorar os becos de todas as cidades entre sua origem e destino, você pode ter várias camadas de dados do mapa: uma camada de 'rodovias' que contém apenas rodovias, uma camada 'secundária' que contém apenas ruas secundárias e assim por diante. Em seguida, você explora apenas seções menores das camadas mais detalhadas, expandindo conforme necessário. Obviamente, essa descrição deixa de fora muitos detalhes, mas você entendeu.

Com modificações nesse sentido, você pode fazer o roteamento até os países em um prazo bastante razoável.

Nick Johnson
fonte
29
Alguém que trabalhou nisso no mundo real, incrível! Você tem alguma idéia do quanto é possível pré-calcular, como no artigo sobre o Google mencionado em outro comentário?
A. Rex
10
Não fizemos nenhum pré-processamento dessa natureza, mas certamente parece uma otimização interessante.
22420 Nick Johnson
29
"só é garantido produzir uma solução, não necessariamente a mais eficiente". Isso é falso; contanto que a heurística usada seja admissível, o algoritmo A * produz o caminho de menor custo. Admissível significa que o custo nunca é superestimado, mas pode ser subestimado (mas estimativas ruins retardarão o algoritmo). O uso de dados do pré-processamento provavelmente ajudará a criar uma heurística admissível melhor e, portanto, tornar o A * mais rápido.
a1kmm 3/09/09
6
Na verdade, em uma análise mais aprofundada, você está totalmente certo. Você pode aprimorar um algoritmo existente para fazer uso disso, simplesmente adicionando a Grande Distância do Círculo entre o nó de destino e o destino ao custo da borda que está sendo avaliada. Na verdade, não tenho certeza se nosso algoritmo fez isso - mas é uma otimização muito sensata.
Nick Johnson
11
A *, no pior caso (uma heurística que diz que todos os caminhos são equivalentes), é exatamente igual ao de Djikstra.
Tordek 26/10/10
111

Esta questão tem sido uma área ativa de pesquisa nos últimos anos. A idéia principal é fazer um pré - processamento no gráfico uma vez , para acelerar todas as consultas a seguir . Com essas informações adicionais, os itinerários podem ser calculados muito rapidamente. Ainda assim, o algoritmo de Dijkstra é a base para todas as otimizações.

Aracnídeo descreveu o uso de pesquisa bidirecional e remoção de borda com base em informações hierárquicas. Essas técnicas de aceleração funcionam muito bem, mas os algoritmos mais recentes superam essas técnicas em todos os aspectos. Com os algoritmos atuais, os caminhos mais curtos podem ser calculados em menos tempo do que um milissegundo em uma rede rodoviária continental. Uma implementação rápida do algoritmo não modificado do Dijkstra precisa de cerca de 10 segundos .

O artigo Algoritmos de planejamento de rota rápida de engenharia fornece uma visão geral do progresso da pesquisa nesse campo. Veja as referências desse documento para mais informações.

Os algoritmos conhecidos mais rápidos não usam informações sobre o status hierárquico da estrada nos dados, ou seja, se é uma estrada ou uma estrada local. Em vez disso, eles calculam em uma etapa de pré-processamento uma hierarquia própria que foi otimizada para acelerar o planejamento de rotas. Essa precomputação pode ser usada para podar a pesquisa: Longe do início e do destino, as estradas lentas não precisam ser consideradas durante o algoritmo de Dijkstra. Os benefícios são um desempenho muito bom e uma garantia de correção para o resultado.

Os primeiros algoritmos de planejamento de rota otimizados lidavam apenas com redes rodoviárias estáticas, o que significa que uma margem no gráfico tem um valor de custo fixo. Isso não é verdade na prática, pois queremos levar em consideração informações dinâmicas, como engarrafamentos ou restrições dependentes de veículos. Os algoritmos mais recentes também podem lidar com esses problemas, mas ainda existem problemas a serem resolvidos e a pesquisa está em andamento.

Se você precisar das distâncias mais curtas do caminho para calcular uma solução para o TSP , provavelmente está interessado em matrizes que contêm todas as distâncias entre suas fontes e destinos. Para isso, você pode considerar Computar os caminhos mais curtos de muitos para muitos usando hierarquias de rodovias . Observe que isso foi aprimorado pelas abordagens mais recentes nos últimos 2 anos.

SebastianK
fonte
1
Iluminador, de fato. Quais são as novas abordagens que você está mencionando?
Tomas Pajonk
1
Em particular, hierarquias de contração. Você pode encontrar mais informações aqui: algo2.iti.kit.edu/routeplanning.php . Há também uma palestra google tecnologia nisso: youtube.com/watch?v=-0ErpE8tQbw
Sebastiank
19

Apenas abordando as violações da desigualdade do triângulo, espero que o fator extra para o qual eles estejam otimizando seja o senso comum. Você não quer necessariamente o caminho mais curto ou mais rápido, pois pode levar ao caos e destruição . Se você deseja que suas direções prefiram as principais rotas que são adequadas para caminhões e podem lidar com o envio de todos os motoristas que seguem por sat-nav, você rapidamente descarta a desigualdade do triângulo [1].

Se Y for uma rua residencial estreita entre X e Z, provavelmente você só deseja usar o atalho via Y se o usuário solicitar explicitamente XYZ. Se pedirem o XZ, devem seguir as principais estradas, mesmo que seja um pouco mais longe e demore um pouco mais. É semelhante ao paradoxo de Braess - se todos tentarem seguir o caminho mais curto e mais rápido, o congestionamento resultante significa que não é mais o caminho mais rápido para ninguém. A partir daqui, passamos da teoria dos grafos para a teoria dos jogos.

[1] De fato, qualquer esperança de que as distâncias produzidas sejam uma função de distância no sentido matemático morre quando você permite estradas de mão única e perde o requisito de simetria. Perder a desigualdade do triângulo também é apenas esfregar sal na ferida.

stevemegson
fonte
14

Aqui estão os algoritmos de roteamento mais rápidos do mundo, comparados e comprovados quanto à correção:

http://algo2.iti.uka.de/schultes/hwy/schultes_diss.pdf

Aqui está uma palestra do google tech sobre o assunto:

http://www.youtube.com/watch?v=-0ErpE8tQbw

Aqui está uma implementação do algoritmo de hierarquias de rodovias, conforme discutido por schultes (atualmente apenas em Berlim, estou escrevendo a interface e uma versão móvel também está sendo desenvolvida):

http://tom.mapsforge.org/

eikes
fonte
9

Eu não trabalhei no Google, Microsoft ou Yahoo Maps antes, então não posso dizer como eles funcionam.

No entanto, eu projetei um sistema personalizado de otimização da cadeia de suprimentos para uma empresa de energia que incluía um aplicativo de agendamento e roteamento para sua frota de caminhões. No entanto, nossos critérios de roteamento eram muito mais específicos do negócio do que onde a construção ou o tráfego diminuem ou o fechamento das faixas.

Empregamos uma técnica chamada ACO (Otimização de colônias de formigas) para agendar e encaminhar caminhões. Essa técnica é uma técnica de IA aplicada ao problema do vendedor ambulante para resolver problemas de roteamento. O truque com o ACO é criar um cálculo de erro com base em fatos conhecidos do roteamento, para que o modelo de solução de gráficos saiba quando sair (quando o erro é pequeno o suficiente).

Você pode pesquisar no Google ACO ou TSP para saber mais sobre essa técnica. No entanto, eu não usei nenhuma das ferramentas de IA de código aberto para isso, então não posso sugerir uma (embora eu tenha ouvido falar que o SWARM era bastante abrangente).

Conta
fonte
4
roteamento! = colher de chá. em tsp, você conhece todas as distâncias e otimiza a ordem de parada - este não é um ponto a ponto.
precisa
8

Algoritmos de gráficos como o algoritmo de Dijkstra não funcionarão porque o gráfico é enorme.

Esse argumento não se aplica necessariamente porque o Dijkstra geralmente não olha o gráfico completo, mas apenas um subconjunto muito pequeno (quanto melhor o gráfico interconectado, menor o subconjunto).

Dijkstra pode realmente ter um bom desempenho em gráficos bem comportados. Por outro lado, com uma parametrização cuidadosa, o A * sempre terá um desempenho tão bom ou melhor. Você já tentou o desempenho dos seus dados?

Dito isto, eu também ficaria muito interessado em ouvir sobre as experiências de outras pessoas. Obviamente, exemplos importantes como a pesquisa do Google Map são particularmente interessantes. Eu poderia imaginar algo como uma heurística do vizinho mais próximo.

Konrad Rudolph
fonte
2
Suponha que você esteja tentando encontrar direções do ponto A a B, cuja distância ideal é d. O algoritmo de Dijkstra examinará, no mínimo, todos os pontos à distância no máximo d de A. Se A é São Francisco e B é Boston, isso significa que examina a maior parte dos EUA. N'est-ce pas?
A.Rex
2
Sim, ele é. O que eu queria entender é que o A * pode ser usado em vez disso e que ele sempre encontra uma solução ideal (mesmo que use uma heurística)!
21978 Konrad Rudolph
Sim, claro. Depois de escrever meu post original, pensei na palavra "heurística" que usei. É um pouco impreciso aqui, porque encontra uma solução ideal.
A.Rex
2
Bem, o ponto é que A * usa uma heurística (em vez de ser uma) para determinar o próximo passo. Essa decisão única pode realmente ser subótima. Mas isso afeta apenas o tempo de execução, não o resultado, uma vez que determina apenas quanto melhor do que Dijstra ele adivinha.
214 Konrad Rudolph
8

O estado da arte atual em termos de tempos de consulta para redes rodoviárias estáticas é o algoritmo de rotulagem de Hub proposto por Abraham et al. http://link.springer.com/chapter/10.1007/978-3-642-20662-7_20 . Uma pesquisa completa e excelentemente escrita do campo foi publicada recentemente como um relatório técnico da Microsoft http://research.microsoft.com/pubs/207102/MSR-TR-2014-4.pdf .

A versão curta é ...

O algoritmo de rotulagem do Hub fornece as consultas mais rápidas para redes rodoviárias estáticas, mas requer uma grande quantidade de RAM para executar (18 GiB).

O roteamento do nó de trânsito é um pouco mais lento, embora exija apenas cerca de 2 GiB de memória e possui um tempo de pré-processamento mais rápido.

As hierarquias de contração oferecem uma boa troca entre tempos rápidos de pré-processamento, requisitos de pouco espaço (0,4 GiB) e tempos rápidos de consulta.

Nenhum algoritmo é completamente dominado ...

Esta palestra sobre tecnologia do Google de Peter Sanders pode ser interessante

https://www.youtube.com/watch?v=-0ErpE8tQbw

Também esta palestra de Andrew Goldberg

https://www.youtube.com/watch?v=WPrkc78XLhw

Uma implementação de código aberto de hierarquias de contração está disponível no site do grupo de pesquisa Peter Sanders no KIT. http://algo2.iti.kit.edu/english/routeplanning.php

Também uma postagem de blog de fácil acesso, escrita pela Microsoft, sobre o uso do algoritmo CRP ... http://blogs.bing.com/maps/2012/01/05/bing-maps-new-routing-engine/

Barnaby Hussey-Yeo
fonte
7

Estou um pouco surpreso por não ver o algoritmo de Floyd Warshall mencionado aqui. Esse trabalho de algoritmo é muito parecido com o de Dijkstra. Ele também possui um recurso muito interessante: permite calcular o tempo que desejar continuar permitindo mais vértices intermediários. Por isso, encontrará naturalmente as rotas que utilizam interestaduais ou rodovias rapidamente.

dennisjtaylor
fonte
6

Eu já fiz isso várias vezes, na verdade, tentando vários métodos diferentes. Dependendo do tamanho (geográfico) do mapa, você pode considerar o uso da função haversine como uma heurística.

A melhor solução que eu fiz foi usar A * com uma distância em linha reta como uma função heurística. Mas então você precisa de algum tipo de coordenada para cada ponto (interseção ou vértice) no mapa. Você também pode tentar diferentes ponderações para a função heurística, ou seja,

f(n) = k*h(n) + g(n)

onde k é alguma constante maior que 0.

Pål GD
fonte
4

Provavelmente semelhante à resposta em rotas pré-calculadas entre locais principais e mapas em camadas, mas meu entendimento é que, em jogos, para acelerar o A *, você tem um mapa muito grosseiro para a navegação em macro e um mapa detalhado para navegação até o limite das direções da macro. Portanto, você tem dois pequenos caminhos para calcular e, portanto, seu espaço de pesquisa é muito menor do que simplesmente fazer um único caminho para o destino. E se você estiver fazendo muito isso, terá muitos dados pré-calculados, pelo menos parte da pesquisa é uma pesquisa de dados pré-calculados, em vez de uma pesquisa de um caminho.

Marcin
fonte
3

Isso é pura especulação da minha parte, mas suponho que eles possam usar uma estrutura de dados de mapas de influência sobrepondo o mapa direcionado para restringir o domínio de pesquisa. Isso permitiria que o algoritmo de busca direcionasse o caminho para as principais rotas quando a viagem desejada fosse longa.

Como esse é um aplicativo do Google, também é razoável supor que grande parte da mágica seja feita por meio de cache extenso. :) Não ficaria surpreso se o armazenamento em cache dos 5% principais pedidos de rotas do Google Map permitisse que uma grande parte (20%? 50%?) Dos pedidos fosse respondida com uma simples pesquisa.

Parappa
fonte
Você tem uma boa referência para "uma estrutura de dados de mapas de influência"? Obrigado!
A.Rex
3

Eu pensei mais um pouco sobre isso:

1) Lembre-se de que os mapas representam uma organização física. Armazene a latitude / longitude de cada interseção. Você não precisa verificar muito além dos pontos que estão na direção do seu alvo. Somente se você estiver bloqueado, precisará ir além disso. Se você armazenar uma sobreposição de conexões superiores, poderá limitá-la ainda mais - normalmente você nunca encontrará uma delas de uma maneira que se afaste do seu destino final.

2) Divida o mundo em várias zonas definidas por conectividade limitada, defina todos os pontos de conectividade entre as zonas. Encontre em quais zonas sua origem e destino estão, para a rota da zona inicial e final da sua localização até cada ponto de conexão, para as zonas entre simplesmente mapear entre os pontos de conexão. (Suspeito que muitos destes já estejam pré-calculados.)

Observe que as zonas podem ser menores que uma área metropolitana. Qualquer cidade com características do terreno que a dividam (digamos, um rio) seria de várias zonas.

Loren Pechtel
fonte
3

Fiquei muito curioso sobre as heurísticas usadas, quando, há algum tempo, recebemos rotas do mesmo local de partida perto de Santa Rosa, para dois acampamentos diferentes no Parque Nacional de Yosemite. Esses destinos diferentes produziram rotas bastante diferentes (via I-580 ou CA-12), apesar do fato de ambas as rotas convergirem pelas últimas 100 milhas (ao longo da CA-120) antes de divergirem novamente por algumas milhas no final. Isso foi bastante repetitivo. As duas rotas estavam separadas por até 80 quilômetros por cerca de 160 quilômetros, mas as distâncias / tempos eram muito próximos um do outro, como seria de esperar.

Infelizmente, não posso reproduzir isso - os algoritmos devem ter mudado. Mas isso me deixou curioso sobre o algoritmo. Tudo o que posso especular é que houve alguma poda direcional que, por acaso, era primorosamente sensível à pequena diferença angular entre os destinos vistos de longe, ou que havia segmentos pré-computados diferentes selecionados pela escolha do destino final.

Zhahai
fonte
3

Falando em GraphHopper , um rápido planejador de rotas de código aberto baseado no OpenStreetMap, li um pouco da literatura e implementei alguns métodos. A solução mais simples é um Dijkstra e uma melhoria simples é um Dijkstra bidirecional que explora aproximadamente apenas a metade dos nós. Com o Dijkstra bidirecional, uma rota por toda a Alemanha já leva 1 segundo (para o modo de carro), em C seria provavelmente apenas 0,5s ou mais;)

Criei um gif animado de uma pesquisa de caminho real com o Dijkstra bidirecional aqui . Além disso, existem mais algumas idéias para tornar o Dijkstra mais rápido, como fazer A *, que é um "Dijkstra orientado a objetivos". Também criei uma animação gif para ele.

Mas como fazê-lo (muito) mais rápido?

O problema é que, para uma pesquisa de caminho, todos os nós entre os locais precisam ser explorados e isso é realmente caro, já que na Alemanha existem vários milhões deles. Mas um ponto de dor adicional de Dijkstra etc é que essas pesquisas usam muita RAM.

Existem soluções heurísticas, mas também soluções exatas que organizam o gráfico (rede rodoviária) em camadas hierárquicas, ambas têm vantagens e desvantagens e resolvem principalmente o problema de velocidade e RAM. Eu listei alguns deles nesta resposta .

Para o GraphHopper, decidi usar as hierarquias de contração porque é relativamente "fácil" de implementar e não leva anos para a preparação do gráfico. Ainda resulta em tempos de resposta muito rápidos, como você pode testar em nossa instância online GraphHopper Maps . Por exemplo, da África do Sul ao leste da China, o que resulta em uma distância de 23000 km e em quase 14 dias de tempo de carro e levou apenas 0,1s no servidor.

Karussell
fonte
O Dijkstra bidirecional que usa o recurso Marcos para executar a pesquisa direcionada por objetivos é mais eficiente que o Dijkstra bidirecional sozinho. Consulte www14.informatik.tu-muenchen.de/lehre/2010SS/sarntal/… No entanto, este documento não é detalhado o suficiente para calcular a função potencial, que é um pouco complicada, ou escolher os pontos de referência.
Paul Chernoch 14/08/2015
2

Eu trabalhei no roteamento por alguns anos, com uma recente explosão de atividade motivada pelas necessidades de meus clientes e descobri que o A * é facilmente rápido o suficiente; não há realmente necessidade de otimizações ou algoritmos mais complexos. Rotear sobre um gráfico enorme não é um problema.

Mas a velocidade depende de ter toda a rede de roteamento, com o que quero dizer o gráfico direcionado de arcos e nós representando segmentos e junções de rota, respectivamente, na memória. O tempo principal principal é o tempo gasto para criar esta rede. Alguns números aproximados baseados em um laptop comum executando o Windows e roteando por toda a Espanha: tempo necessário para criar a rede: 10 a 15 segundos; tempo necessário para calcular uma rota: muito curto para medir.

A outra coisa importante é poder reutilizar a rede para os cálculos de roteamento que você desejar. Se o seu algoritmo marcou os nós de alguma forma para registrar a melhor rota (custo total para o nó atual e o melhor arco para ele) - como em A * -, você deve redefinir ou limpar essas informações antigas. Em vez de passar por centenas de milhares de nós, é mais fácil usar um sistema de números de geração. Marque cada nó com o número de geração de seus dados; aumente o número de geração ao calcular uma nova rota; qualquer nó com um número de geração mais antiga é obsoleto e suas informações podem ser ignoradas.

Graham Asher
fonte
1

Vejo o que há com os mapas no OP:

Observe a rota com o ponto intermediário especificado: a rota segue ligeiramente para trás devido a essa estrada que não é reta.

Se o algoritmo deles não voltar atrás, ele não verá a rota mais curta.

Loren Pechtel
fonte
Idéia interessante. Adicionei outra violação no meu PPS ao OP. Por favor, dê uma olhada e veja se você pode ver uma explicação lá.
A. Rex
Zoom MUITO no ponto A - a um clique do max. Observe como a rota de três pontos segue para oeste, sul e leste. Acho que estamos analisando um algoritmo que não gosta de voltar atrás, a menos que seja necessário passar por um ponto de estrangulamento.
Loren Pechtel 10/01/09
No meu exemplo de PPS, altere o endereço inicial para "10 Avenue de Flandre, 75019 Paris". Isso remove o pequeno retorno que você está falando, mas o problema ainda está lá. Eu acho que o principal problema é que ele realmente quer ficar em que o principal Blvd ...
A. Rex
1
Acho que encontrei neste caso: aqueles de carro e os horários fazem sentido. Provavelmente vê a estrada grande como mais rápida e a rota a pé não a estrangula.
Loren Pechtel
1
PS: O problema inicial também faz sentido para esse padrão; pode não ser o retorno que o causou.
Loren Pechtel
0

Um algoritmo de caminho mais curto para todos os pares calculará os caminhos mais curtos entre todos os vértices em um gráfico. Isso permitirá que os caminhos sejam pré-calculados em vez de exigir que um caminho seja calculado toda vez que alguém desejar encontrar o caminho mais curto entre uma origem e um destino. O algoritmo de Floyd-Warshall é um algoritmo de caminho mais curto para todos os pares.

J. Michael Wuerth
fonte
-4

Os mapas nunca levam em consideração o mapa inteiro. Meu palpite é: - 1. De acordo com a sua localização, eles carregam um local e os pontos de referência nesse local. 2. Quando você pesquisa o destino, é quando eles carregam a outra parte do mapa e criam um gráfico em dois lugares e, em seguida, aplicam os algoritmos de caminho mais curto.

Além disso, existe uma técnica importante Programação dinâmica que, suspeito, é usada no cálculo dos caminhos mais curtos. Você pode se referir a isso também.

Yogesh kumar
fonte