簡體   English   中英

是否可以將boost庫的廣度優先搜索算法應用於矩陣?

[英]Is it possible to apply breadth-first search algorithm of boost library to matrix?

我的任務是在矩陣中找到從一個點到另一個點的最短路徑。 可以僅在這樣的方向上移動(向上,向下,向左,向右)。

0 0 0 0 1 0 0 0
1 0 0 0 0 0 0 0
0 0 0 1 0 1 F 0
0 1 0 1 0 0 0 0
0 0 0 1 0 0 0 0
0 S 0 1 0 0 1 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 1 0

S - 起點

F - 目的地(完成)

0 - 免費細胞(我們可以穿過它們)

1 - “牆壁”(我們不能穿過它們)

很明顯,廣度優先搜索以最佳方式解決了這個問題。 我知道Boost庫提供了這個算法,但我之前沒有Boost。

如何使用Boost在我的案例中進行廣度優先搜索? 據我所知,Boost的廣度優先搜索算法僅適用於圖形。 我想用m*n頂點和m*(n -1) + (m-1)*n邊緣將矩陣轉換為圖形不是一個好主意。

我可以將廣度優先搜索算法應用於矩陣(不將其轉換為圖形),還是更好地實現我自己的廣度優先搜索功能?

(對於這個答案的長度提前道歉。自從我使用BGL以來已經有一段時間了,我認為這樣可以很好地復習。完整代碼就在這里 。)

Boost圖形庫(以及通用編程)的優點在於您不需要使用任何特定的數據結構來利用給定的算法。 您提供的矩陣以及有關遍歷它的規則已經定義了一個圖形。 所需要的只是將這些規則編碼在可用於利用BGL算法的traits類中。

具體來說,我們要做的是為您的圖形定義boost::graph_traits<T>的特化。 假設您的矩陣是行主格式的單個int數組。 不幸的是,為int[N]專門化graph_traits是不夠的,因為它不提供有關矩陣維度的任何信息。 因此,讓我們按如下方式定義您的圖表:

namespace matrix
{
  typedef int cell;

  static const int FREE = 0;
  static const int WALL = 1;

  template< size_t ROWS, size_t COLS >
  struct graph
  {
    cell cells[ROWS*COLS];
  };
}

我在這里使用了組合作為單元數據,但如果要在外部進行管理,你可以很容易地使用指針。 現在我們有一個用矩陣維度編碼的類型,可以用來專門化graph_traits 但首先讓我們定義一些我們需要的功能和類型。

頂點類型和輔助函數:

namespace matrix
{
  typedef size_t vertex_descriptor;

  template< size_t ROWS, size_t COLS >
  size_t get_row(
    vertex_descriptor vertex,
    graph< ROWS, COLS > const & )
  {
    return vertex / COLS;
  }

  template< size_t ROWS, size_t COLS >
  size_t get_col(
    vertex_descriptor vertex,
    graph< ROWS, COLS > const & )
  {
    return vertex % COLS;
  }

  template< size_t ROWS, size_t COLS >
  vertex_descriptor make_vertex(
    size_t row,
    size_t col,
    graph< ROWS, COLS > const & )
  {
    return row * COLS + col;
  }
}

遍歷頂點的類型和函數:

namespace matrix
{
  typedef const cell * vertex_iterator;

  template< size_t ROWS, size_t COLS >
  std::pair< vertex_iterator, vertex_iterator >
  vertices( graph< ROWS, COLS > const & g )
  {
    return std::make_pair( g.cells, g.cells + ROWS*COLS );
  }

  typedef size_t vertices_size_type;

  template< size_t ROWS, size_t COLS >
  vertices_size_type
  num_vertices( graph< ROWS, COLS > const & g )
  {
    return ROWS*COLS;
  }
}

邊緣類型:

namespace matrix
{
  typedef std::pair< vertex_descriptor, vertex_descriptor > edge_descriptor;

  bool operator==(
    edge_descriptor const & lhs,
    edge_descriptor const & rhs )
  {
    return
      lhs.first == rhs.first && lhs.second == rhs.second ||
      lhs.first == rhs.second && lhs.second == rhs.first;
  }

  bool operator!=(
    edge_descriptor const & lhs,
    edge_descriptor const & rhs )
  {
    return !(lhs == rhs);
  }
}

最后,迭代器和函數幫助我們遍歷頂點和邊之間存在的關聯關系:

namespace matrix
{
  template< size_t ROWS, size_t COLS >
  vertex_descriptor
  source(
    edge_descriptor const & edge,
    graph< ROWS, COLS > const & )
  {
    return edge.first;
  }

  template< size_t ROWS, size_t COLS >
  vertex_descriptor
  target(
    edge_descriptor const & edge,
    graph< ROWS, COLS > const & )
  {
    return edge.second;
  }

  typedef boost::shared_container_iterator< std::vector< edge_descriptor > > out_edge_iterator;

  template< size_t ROWS, size_t COLS >
  std::pair< out_edge_iterator, out_edge_iterator >
  out_edges(
    vertex_descriptor vertex,
    graph< ROWS, COLS > const & g )
  {
    boost::shared_ptr< std::vector< edge_descriptor > > edges( new std::vector< edge_descriptor >() );

    if( g.cells[vertex] == FREE )
    {
      size_t
        row = get_row( vertex, g ),
        col = get_col( vertex, g );

      if( row != 0 )
      {
        vertex_descriptor up = make_vertex( row - 1, col, g );

        if( g.cells[up] == FREE )
          edges->push_back( edge_descriptor( vertex, up ) );
      }

      if( row != ROWS-1 )
      {
        vertex_descriptor down = make_vertex( row + 1, col, g );

        if( g.cells[down] == FREE )
          edges->push_back( edge_descriptor( vertex, down ) );
      }

      if( col != 0 )
      {
        vertex_descriptor left = make_vertex( row, col - 1, g );

        if( g.cells[left] == FREE )
          edges->push_back( edge_descriptor( vertex, left ) );
      }

      if( col != COLS-1 )
      {
        vertex_descriptor right = make_vertex( row, col + 1, g );

        if( g.cells[right] == FREE )
          edges->push_back( edge_descriptor( vertex, right ) );
      }
    }

    return boost::make_shared_container_range( edges );
  }

  typedef size_t degree_size_type;

  template< size_t ROWS, size_t COLS >
  degree_size_type
  out_degree(
    vertex_descriptor vertex,
    graph< ROWS, COLS > const & g )
  {
    std::pair< out_edge_iterator, out_edge_iterator > edges = out_edges( vertex, g );
    return std::distance( edges.first, edges.second );
  }
}

現在我們准備定義boost::graph_traits專業化:

namespace boost
{
  template< size_t ROWS, size_t COLS >
  struct graph_traits< matrix::graph< ROWS, COLS > >
  {
    typedef matrix::vertex_descriptor vertex_descriptor;
    typedef matrix::edge_descriptor edge_descriptor;

    typedef matrix::out_edge_iterator out_edge_iterator;
    typedef matrix::vertex_iterator vertex_iterator;

    typedef boost::undirected_tag directed_category;
    typedef boost::disallow_parallel_edge_tag edge_parallel_category;
    struct traversal_category :
      virtual boost::vertex_list_graph_tag,
      virtual boost::incidence_graph_tag {};

    typedef matrix::vertices_size_type vertices_size_type;
    typedef matrix::degree_size_type degree_size_type;

    static vertex_descriptor null_vertex() { return ROWS*COLS; }
  };
}

以下是如何執行廣度優先搜索並找到最短路徑:

int main()
{
  const size_t rows = 8, cols = 8;

  using namespace matrix;

  typedef graph< rows, cols > my_graph;

  my_graph g =
  {
    FREE, FREE, FREE, FREE, WALL, FREE, FREE, FREE,
    WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE,
    FREE, FREE, FREE, WALL, FREE, WALL, FREE, FREE,
    FREE, WALL, FREE, WALL, FREE, FREE, FREE, FREE,
    FREE, FREE, FREE, WALL, FREE, FREE, FREE, FREE,
    FREE, FREE, FREE, WALL, FREE, FREE, WALL, FREE,
    FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE,
    FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE,
  };

  const vertex_descriptor
    start_vertex = make_vertex( 5, 1, g ),
    finish_vertex = make_vertex( 2, 6, g );

  vertex_descriptor predecessors[rows*cols] = { 0 };

  using namespace boost;

  breadth_first_search(
    g,
    start_vertex,
    visitor( make_bfs_visitor( record_predecessors( predecessors, on_tree_edge() ) ) ).
    vertex_index_map( identity_property_map() ) );

  typedef std::list< vertex_descriptor > path;

  path p;

  for( vertex_descriptor vertex = finish_vertex; vertex != start_vertex; vertex = predecessors[vertex] )
    p.push_front( vertex );

  p.push_front( start_vertex );

  for( path::const_iterator cell = p.begin(); cell != p.end(); ++cell )
    std::cout << "[" << get_row( *cell, g ) << ", " << get_col( *cell, g ) << "]\n" ;

  return 0;
}

從開始到結束沿最短路徑輸出單元格:

[5, 1]
[4, 1]
[4, 2]
[3, 2]
[2, 2]
[1, 2]
[1, 3]
[1, 4]
[1, 5]
[1, 6]
[2, 6]

你絕對可以使用Boost Graph庫! 實現此庫中的算法的想法是從任何圖形數據結構中抽象出來,而是根據迭代器進行操作。 例如,要從一個節點移動到另一個節點,算法使用鄰接迭代器。 你本質上看起來和一個特定的算法,例如BFS ,並找出這個算法需要什么概念:在這種情況下,你使用它的圖需要是“頂點列表圖”和“發生率圖”。 請注意,這些不是具體類,而是概念:它們指定了如何訪問數據結構。 該算法還采用了許多其他參數,如起始節點和屬性映射,以標記(顏色)已訪問過的節點。

要將算法與矩陣一起使用,您可以為算法提供矩陣的“圖形視圖”:除非相應的鄰居設置為1,否則節點與其直接鄰居相鄰(顯然,您不會離開矩陣的邊緣)。 創建這樣的圖形並非完全無足輕重,但我認為理解Boost Graph庫的工作方式非常有用:即使您不想使用這個特定的庫,它也是一個很好的例子,說明如何在即使在完全不可預見的情況下也能使算法適用的抽象(好吧,我有偏見:早在Jeremy創建Boost Graph庫之前,我已經寫了大約相同的文憑論文,我們提出了基本相同的抽象)。

總而言之,我認為使用廣度優先搜索可能不值得學習Boost Graph庫:它是一個非常簡單的算法,你可能想直接實現它。 此外,這看起來非常像家庭作業,在這種情況下,您可能想要自己實施算法。 雖然為此使用Boost Graph庫可能會令人印象深刻,但您的教師可能不會這樣考慮它。 我認為更令人印象深刻的是在Boost Graph庫中使用獨立於數據結構的樣式實現BFS,然后使用它。 在Boost Graph庫的指導下,這絕對是可行的,即使是練習(盡管可能比需要的更多努力)。 順便說一句,是的,我可以發布代碼但是,不,我不會。 不過,我很樂意幫助解決發布的具體問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM