弗洛伊德算法-Floyd(Floyd-Warshall)-求多源最短路徑,求傳遞閉包
Floyd算法又稱為插點法,是一種利用動態規划的思想尋找給定的加權圖中多源點之間最短路徑的算法,
與Dijkstra算法類似。該算法名稱以創始人之一、1978年圖靈獎獲得者、斯坦福大學計算機科學系教授羅伯特·弗洛伊德命名。
為什么要求傳遞閉包?
因為:一個有n個頂點的有向圖的傳遞閉包為:有向圖中的初始路徑可達情況可以參見其鄰接矩陣A,
鄰接矩陣中A[i,j]表示i到j是否直接可達,若直接可達,則A[i,j]記為1,否則記為0;兩個有向圖
中i到j有路徑表示從i點開始經過其他點(或者不經過其他點)能夠到達j點,如果i到j有路徑,
則將T[i,j]設置為1,否則設置為0;有向圖的傳遞閉包表示從鄰接矩陣A出發,求的所有節點
間的路徑可達情況,該矩陣就為所要求的傳遞閉包矩陣
warshall傳遞閉包算法的目的:就是由鄰接矩陣出發,進行探索求出最終的傳遞閉包
(i是行,j是列)
算法過程:
(1)i=1時,第一列有A[4,1]=1,將第四行元素分別與第一行對應元素進行
邏輯加(或運算):
0 1 0 0
0 0 0 1
0 0 0 0
1 1 1 0
(2)i=2時,第二列有A[1,2]=1,A[4,2]=1,將第一行元素和第四行元素分別與第二行
對應元素進行邏輯加:
0 1 0 1
0 0 0 1
0 0 0 0
1 1 1 1
(3)i=3時,第三列有A[4,3]=1,將第四行元素分別與第三行對應元素進行邏輯加:
0 1 0 1
0 0 0 1
0 0 0 0
1 1 1 1
(4)i=4時,第四列有A[1,4]=1,A[2,4]=1,A[4,4]=1,將第一行元素、第二行
元素和第四行元素分別與第四行對應元素進行邏輯加:
1 1 1 1
1 1 1 1
0 0 0 0
1 1 1 1
Python核心代碼:
Matrix = [] #聲明空矩陣
n = int(input('請輸入矩陣階數: \n')) #將輸入的數字整型化賦給n
#獲取矩陣關系
for i in range(n): #從0到n-1依次取值
Matrix.append(input('第{}行'.format(i+1)).split())
def logicadd(a,b):
#邏輯加(或運算)
if a==0 and b==0:
return 0
else:
return 1
#Walshall算法求傳遞閉包
for column in range(n): #從第一列到第n列[range()函數從0到n-1但是不影響算法]
for row in range(n): #從第一行到第n行[range()函數從0到n-1但是不影響算法]
#row的for循環在column的for循環的下面,在行數確定時,對相應列的所有元素進行遍歷,變化的是row行數
if int(Matrix[row][column])==1: #判斷第row行第column行的元素是否為1
for i in range(n): #計算n次
Matrix[row][i]=logicadd(int(Matrix[row][i]),int(Matrix[column][i]))
#將該行的所有元素與對應行的元素進行邏輯加運算,此處,因為行數與列數是相同的,所以用column固定值表示
print(Matrix)
#該算法的核心是:從矩陣的第一行開始,查看第一列的元素,如果有值為1,則將該1值所處的行數的所有元素與第一行的對應元素進行邏輯加運算;依次計算......
另外一種思想:
如果知道點數,知道邊數以及邊的方向,該如何求出傳遞閉包?
請思考:k階應該在最里層,還是最外層,為什么? 如何體現 i->k 和 k->j的與?
map()函數的格式是:
map(function,iterable,...)
第一個參數接受一個函數名,后面的參數接受一個或多個可迭代的序列,返回的是一個集合。
把函數依次作用在list中的每一個元素上,得到一個新的list並返回。
用法:zeros(shape, dtype=float, order='C')
返回:返回來一個給定形狀和類型的用0填充的數組;
shape:形狀
dtype:數據類型,可選參數,默認numpy.float64
order:可選參數,c代表與c語言類似,行優先;F代表列優先
range(10)表示: range(0, 10)
python編程代碼如下:
import numpy as np
def Warshall(A,n):
for k in range(n):
for i in range(n):
for j in range(n):
A[i][j] = A[i][j] or (A[i][k] and A[k][j]) #k-1階的時候,A[i,j]如果是1,那么就似乎A[i,j] = A[i,j],如果A[i,j]是0,再看 A[i,j] = A[i,k] and A[k,j]
return A #i相當於1,k相當於2,j相當於3;若有從1到3的直接路徑,則覆蓋。若只有從1到2再到3的間接路徑,則取后面的間接路徑,間接路徑成立的條件是從1到2和從2到3都成立,所以是and
n,m=map(int,input("請輸入點數n和邊數m:").split()) #將點數和邊數整型化后賦給n,m
A=np.zeros((n,n),dtype=np.int32)
for i in range(0,m): #同range(m)
a,b=map(int,input("請輸入有向邊的頂點a->b:").split()) #將有向邊的頂點數字整型化后賦給a,b
A[a-1][b-1]=1
print("鄰接矩陣為: \n",A)
print("最終的傳遞閉包為: \n",Warshall(A,n))