Google Treasure Hunt 3: Busca em Profundidade (Depth-First Search)
Autor: Rosfran Borges | Publicado: 2008-08-16T01:20:00-03:00

Esse é outro problema do Google Treasure Hunt que eu resolvi. Esse foi o mais fácil de todos, e realmente não era necessário nenhum conhecimento de programação. Tenha-se em mente apenas que o não uso de programação para resolvê-lo não é de modo algum divertido! Tente ensinar seu computador a fazê-lo por você… O problema que eu obtive no Google foi o seguinte:  “Below is a diagram of a computer network. The nodes are hosts on the network, and the lines between them are links. A packet is sent out from host P with a destination of 4.229.231.150. Which nodes does the packet pass through on its way to the destination? (include start and final node in your answer)”  O diagrama para o modelo da rede acima foi fornecido na forma da tabela de roteamento a seguir:  “  A                  26.40.28.17                     75.174.216.140 => 126.186.188.58    26.40.28.17 => 17.100.195.84            4.229.231.0/24 => 75.174.216.140                      13.157.234.26  B                  13.157.234.26                 76.246.145.72 => 53.12.68.161          126.186.188.58 => 75.174.216.140    53.12.68.0/24 => 26.40.28.17                     17.100.195.84  C                  53.12.68.161                   75.174.216.140 => 219.88.219.35      229.23.61.42 => 13.157.234.26          17.100.195.0/24 => 103.19.57.145                     126.186.188.58  D                  103.19.57.145                 86.60.122.237 => 86.60.122.237        117.146.177.141 => 53.12.68.161      229.23.61.0/24 => 219.88.219.35                     212.222.1.12  E                  4.229.231.150                 4.229.231.150 => 103.19.57.145        54.35.7.207 => 54.35.7.207                126.186.188.0/24 => 199.223.210.72 229.23.61.42  F                  54.35.7.207                     4.229.231.150 => 229.23.61.42          13.157.234.26 => 199.223.210.72      26.40.28.0/24 => 4.229.231.150                     86.60.122.237  G                 86.60.122.237                 229.23.61.42 => 54.35.7.207              13.157.234.26 => 103.19.57.145        103.19.57.0/24 => 212.222.1.12                     219.88.219.35  H                 212.222.1.12                   126.186.188.58 => 219.88.219.35      75.174.216.140 => 86.60.122.237      103.19.57.0/24 => 103.19.57.145                     229.23.61.42  I                   229.23.61.42                   4.229.231.150 => 4.229.231.150        117.146.177.141 => 54.35.7.207        126.186.188.0/24 => 199.223.210.72 212.222.1.12  J                   199.223.210.72               53.12.68.161 => 229.23.61.42            4.229.231.150 => 54.35.7.207            219.88.219.0/24 => 219.88.219.35                     4.229.231.150  K                  219.88.219.35                 4.229.231.150 => 199.223.210.72      17.100.195.84 => 212.222.1.12          126.186.188.0/24 => 86.60.122.237                     103.19.57.145  L                  117.146.177.141             4.229.231.150 => 219.88.219.35        117.146.177.141 => 126.186.188.58  26.40.28.0/24 => 76.246.145.72                     75.174.216.140  M                 75.174.216.140               199.223.210.72 => 13.157.234.26      4.229.231.150 => 117.146.177.141    126.186.188.0/24 => 17.100.195.84                     26.40.28.17  N                 17.100.195.84                 4.229.231.150 => 26.40.28.17            219.88.219.35 => 13.157.234.26        17.100.195.0/24 => 75.174.216.140                     76.246.145.72  O                 76.246.145.72                 4.229.231.150 => 17.100.195.84        86.60.122.237 => 126.186.188.58      13.157.234.0/24 => 53.12.68.161                     117.146.177.141  P                  126.186.188.58               13.157.234.26 => 53.12.68.161          4.229.231.150 => 76.246.145.72        103.19.57.0/24 => 26.40.28.17                     117.146.177.141  ”  Como você talvez tenha suposto, o primeiro símbolo é o ID do nó de rede, seguido por seu número IP. A seguir, você tem uma sequência de 3 regras de roteamento, o nó pode ver se essa regra em específico serve para o pacote entrante (incoming packet, ou pacote de rede no estágio de ingresso ao roteador). O ultimo número IP nos dá a rota padrão (“default route”), apenas no caso em que esse nó não encontrou nenhuma regra de roteamento que coubesse para um dado pacote (a rota padrão é como um “else” após uma sequência de “IF”s...).No caso do pacote chegar à rota padrão, esse pacote será direcionado para o host definido pelo endereço IP na última coluna da tabela acima.  Criei uma solução em C++, que usa um método de busca em profundidade para buscar por um caminho válido saindo de um dado nó para outro no modelo de rede do problema. Minha idéia foi fazer a função de  busca em profundidade (em ingles, “depth-first search”) andar de nó em nó (nós são rotulados como: A, B, C, etc.), iterando sobre cada uma das regras da tabela de roteamento, e procurando por cada uma dessas regras que possa rotear adequadamente o pacote através da rede. A função abaixo é auto-explicável:  “  bool RoutingTable::routeTo( string node, string* resp )   {          bool do_it_again = true;                    RoutingEntry rentry = findRouteEntry( node );                    if ( rentry.doneThis() )                 return false;                    resp->append( rentry.getId() );             if ( rentry.getAddress().compare(getFinalDestination()) == 0 ) {                 return false;          }                    vector<RoutingRule> routing_rules = rentry.getRules();          vector<RoutingRule>::iterator i;                    rentry.markThis(true);          // go through each of the routing rules          for ( i = routing_rules.begin(); ( i != routing_rules.end() ) &amp;&amp;                         do_it_again; i++ )           {                 if ( (*i).canRoute(getFinalDestination()) ) {                         do_it_again = routeTo( (*i).getRedirection(), resp );                 } // if                          } // for          rentry.markThis(false);          // no rules; asks the default gateway…          if ( do_it_again )          {                 do_it_again = routeTo( rentry.getDefaultRoute().getRedirection(),                                resp );          }                    return do_it_again;            }  ”  Nessa solução usei apenas a STL do C++, ou C++ Standard Template Library (STL). Todos os nós são armazenados em estruturas como elementos de um vector, e são localizadas por símbolos de ID (A, B, C, etc.), ou por números IP (método findRouteEntry()). Cada nó tem a sua própria estrutura “Rules” (que você pode obter chamando getRules()). Uma vez processadas, cada uma dessas regras pode apontar para um outro nó da tabela, e a função routeTo pode atuar de forma recursiva sobre uma dada regra. Se nenhuma regra suficiente para combiner com o pacote for encontrada, a função routeTo é chamada sobre o nó retornado pela chamada a “getDefaultRoute”:  “          // no rules; asks the default gateway…          if ( do_it_again )          {                 do_it_again = routeTo( rentry.getDefaultRoute().getRedirection(),                                resp );          }  “  As chamadas para a função markThis() são para evitar recursões infinitas (iterar sobre os mesmos nós, de uma forma circular e muitas vezes irreversível, levando ao esgotamento de recursos computacionais). A semântica é simples: markThis(true), quando o código precisar entrar numa “regão crítca”, e markThis(false) quando tiver saído da região crítica.  Você conseguiria encontrar a diferença entre o método de roteamento que eu formulei para o que é implementado por roteadores de rede de verdade (de fabricantes como Cisco)? Talvez meus posts anteriores com questões da certificação CCNA da Cisco possam ajudar...