MPI並行編程系列之五:圖的單源最短路徑演算法

來源:互聯網
上載者:User

    圖的單源最短路徑問題是指求一個從指定的頂點s到其他所有頂點i之間的最短距離。因為是從一點到其他頂點的距離,所以稱謂單源。本文將介紹dijkstra演算法:按路徑長度遞增次序的最短路徑演算法,並給出了對該演算法MPI並行化演算法。

一、演算法思想 

    該演算法的思想是:引入一個輔助向量D,它的每個分量D[i]為源頂點v到其他頂點v[i]的路徑長度。初始態為:如有從v到vi的路徑,則D[i]為弧[v,vi]的權值;否則D[i]為無窮大。顯然

                  D[j] = min{D[i]}

為頂點v出發到其他頂點的一條最短路徑的長度,其路徑為(v,vj)。

    那麼下一條最短路徑長度如何計算呢?其實很簡單,下一條最短路徑長度要麼是源頂點v直接到某一頂點vk的長度,即{v,vk}。要麼是源頂點v經過頂點vj到某一頂點的長度,即{v,vj,vk}。

   我們假設S為已經求得最短路徑的頂點的集合,那麼下一條最短路徑(設其終點為x),要麼是弧{v, vx},要麼為中間只經過S中頂點而最後到達終點X的路徑。

    在一般情況下,下一條最短路徑的長度為:

                 D[j] = min{D[i] | vi 屬於 V-S}

    其中V為圖頂點的集合, D[i]為弧{v, vi}的權值,或者為D[k]和弧{vk, vi}權值之和。

二、演算法描述

     根據以上思想,我們得到演算法描述如下:

輸入:圖G的鄰接矩陣m[0...n-1][0...n-1],源頂點 v

輸出:最短路徑值向量D[0...n-1], 最短路徑矩陣p[0...n-1][0...n-2].其中D[i]為源頂點v到頂點vi的最短路徑長度,向量p[i]為源頂點v到頂點vi的最短路徑

1)初始化D[i]

     D[i] = m[v][vi] == 無窮大 ? 無窮大 : m[v][vi]

2)計算當前最短路徑值

     min = min{D[i]}

     final[i] = 1   //標記頂點i已經取得最短路徑

3)更新最短路徑值及最短路徑

     for(i = 0; i < n; i++)

         if(!final[i])

             if(D[i] > min + m[vk][vi])

                 D[i] = min + m[vk][vi];

             end if

          endif

         for( j = 0; j < n-1 ; j++)

               if(p[vk][j] != 無窮大)

                   p[vi][j] = p[vk][j];

              end if

         end for

         p[vi][j] = vi

     end for

4) 輸出最短路徑和最短路徑值

三、演算法實現

     這裡只給出演算法的核心代碼, 如下:

   1:  void short_path_function(
   2:          int **matrix,
   3:          int vertex_num,
   4:          int vertex_source){
   5:   
   6:      int *short_path_value;
   7:      int *final;
   8:   
   9:      int *short_path_storage;
  10:      int **short_path;
  11:   
  12:      int min;
  13:      int vertex;
  14:   
  15:      int i,j,k;
  16:   
  17:      //分配空間
  18:      short_path_value = my_malloc(sizeof(int) * vertex_num);
  19:      final = my_malloc(sizeof(int) * vertex_num);
  20:      dynamic_allocate_matrix((void *)&short_path, (void *)&short_path_storage, vertex_num,
  21:              vertex_num-1, sizeof(int));
  22:   
  23:      //初始化
  24:      for(i = 0; i < vertex_num; i++){
  25:          final[i] = 0;
  26:          short_path_value[i] = matrix[vertex_source][i];
  27:   
  28:          //設定空路徑
  29:          for(j = 0; j < vertex_num-1; j++)
  30:              short_path[i][j] = -1;
  31:   
  32:          //初始化路徑
  33:          if(short_path_value[i] < MAX_VAULE)
  34:              short_path[i][0] = i;
  35:      }
  36:   
  37:      final[vertex_source] = 1;
  38:      for(i = 1; i < vertex_num; i++){
  39:          //找出從源頂點出發的一條最短路徑長度頂點
  40:          min =MAX_VAULE ;
  41:          for(j = 0; j < vertex_num; j++)
  42:              if(!final[j])
  43:                  if(short_path_value[j] < min){
  44:                      min = short_path_value[j];
  45:                      vertex = j;
  46:                  }
  47:   
  48:          final[vertex] = 1;
  49:   
  50:          //跟新最短路徑
  51:          for(j = 0; j < vertex_num; j++){
  52:              if(!final[j])
  53:                  if(min + matrix[vertex][j] < short_path_value[j]){
  54:                      //跟新最短路徑長度
  55:                      short_path_value[j] = min + matrix[vertex][j];
  56:   
  57:                      //更新路徑
  58:                      k = 0;
  59:                      while(short_path[vertex][k] != -1){
  60:                          short_path[j][k] = short_path[vertex][k];
  61:                          k++;
  62:                      }
  63:   
  64:                      short_path[j][k] = j;
  65:                  }
  66:          }
  67:      }
  68:   
  69:      //列印結果
  70:      array_int_print(vertex_num, short_path_value);
  71:      print_int_matrix(short_path, vertex_num-1, vertex_num);
  72:  }
 
四、並行演算法描述

      我們對這個演算法進行並行化分析,顯然初始化向量D(對應於演算法的第一步),更新最短路徑值和最短路徑(對應於演算法的第三步)是可以並行化實現的,因為只要有了當前最短路徑和最短路徑值,各個頂點的最短路徑的演算法是相互獨立的。在本並行化演算法中,如何求得當前的最短路徑和最短路徑值成為關鍵。

      我們假設一共用P個進程,圖右n個頂點,我們讓每個進程負責n/p個頂點,每個進程都有自己的向量D和最短路徑p,我們如何求得當前的最短路徑長度和最短路徑呢?首先,我們可以計算出各個進程的當前最短路徑,並將局部最短路徑發往進程0,進程0對這些進程的最短路徑進行比較,取得當前的全域最短路徑,並將這一結果廣播到所有的進程。

      其演算法描述如下:

      我們假設總共有p個進程

      輸入:圖G的鄰接矩陣m[0...n-1][0...n-1],源頂點 v

      輸出:最短路徑值向量D[0...n-1], 最短路徑矩陣p[0...n-1][0...n-2].

    1) 進程0讀取鄰接矩陣m和源節點v,並將m,v廣播到其他所有的進程

    2) 各進程並行初始化各自的局部D和P

    3)求最短路徑

         1)各進程並行計算出各自的局部最短路徑值,並將其發送0號進程

         2)0號進程求出全域最短路徑值和對應的進程號,並將其廣播到其他所有的進程。

         3)擁有全域最短路徑值的進程將其對應的最短路徑廣播到其他進程(用於更新各個進程的最短路徑)

    4)各進程並行跟新各自的D和P

五、 並行演算法實現

    我們這裡只給出演算法中第三步的具體實現:

   1:  int get_short_path_value(
   2:          int *final,
   3:          int *vertex,
   4:          int **short_path,
   5:          int *short_path_value,
   6:          int *short_path_copy,
   7:          int *shortest,
   8:          int vertex_num,
   9:          int local_vertex_num,
  10:          int process_id,
  11:          int process_size,
  12:          MPI_Comm comm){
  13:   
  14:      int min;
  15:      int local_vertex;
  16:   
  17:      int shortest_process_id;
  18:   
  19:      MPI_Status status;
  20:   
  21:      int j;
  22:      
  23:      //取到每個進程的路徑的最小值
  24:      min = MAX_VAULE;
  25:      for(j = 0; j < local_vertex_num; j++)
  26:          if(!final[j])
  27:              if(short_path_value[j] < min){
  28:                  min = short_path_value[j];
  29:                  local_vertex = j;
  30:              }
  31:   
  32:      //各進程將自己的最小路徑值發往0號進程
  33:      if(process_id)
  34:           MPI_Send(&min, 1, MPI_INT, 0, DATA_MESSAGE, comm); 
  35:      else{
  36:          //0號進程收集所有進程的最小值
  37:          shortest[0] = min;
  38:          for(j = 1; j < process_size; j++)
  39:              MPI_Recv(shortest+j, 1, MPI_INT, j, DATA_MESSAGE, comm, &status);
  40:   
  41:           //0號進程計算出全域最小值,並廣播到其他各個進程
  42:            min = shortest[0];
  43:            shortest_process_id = 0;
  44:            for(j = 1; j < process_size; j++)
  45:               if(shortest[j] < min){
  46:                   min = shortest[j];
  47:                   shortest_process_id = j;
  48:                }
  49:      }
  50:      //0號進程將最小值廣播到所有進程,如果最小值為無窮大,則返回
  51:      MPI_Bcast(&min, 1, MPI_INT, 0, comm);
  52:      if(min == MAX_VAULE)
  53:          return min;
  54:   
  55:      //0號進程將最小值所屬的進程廣播到所有的進程
  56:      MPI_Bcast(&shortest_process_id, 1, MPI_INT, 0, comm);
  57:   
  58:      //擁有最小值的進程,將其對應的標誌位標1,並計算該座標的全域座標
  59:      if(process_id == shortest_process_id){
  60:          final[local_vertex] = 1;
  61:          *vertex = BLOCK_LOW(shortest_process_id, process_size, vertex_num) + local_vertex;
  62:   
  63:          //複製最短路徑
  64:          for(j = 0; j < vertex_num -1; j++)
  65:              short_path_copy[j] = short_path[local_vertex][j];
  66:      }
  67:   
  68:      //廣播最短路徑
  69:      MPI_Bcast(short_path_copy, vertex_num-1, MPI_INT, shortest_process_id,comm);
  70:      MPI_Bcast(vertex, 1, MPI_INT, shortest_process_id, comm);
  71:   
  72:      return min;
  73:  }
 
六、MPI函數說明

     本並行演算法用到的MPI函數主要為點對點通訊函數MPI_Send和MPI_Recv,全域通訊函數MPI_Bcast。MPI名字起的真好,訊息傳遞編程,我覺得MPI演算法的核心就是任務的劃分和任務間的通訊。

  下篇將介紹圖的最下產生樹及其並行演算法。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.