Python実装--K最短パスアルゴリズム


1枚の図の中でK本の最短経路(短い経路)を探すことを実現する.
 
コードテストの過程で学んだPythonの基礎知識:
1、Python文字列の切り取り操作:
str[0:3]#0位から2位の文字を切り取る
str[:#カット文字列のすべての文字
str[6]#7文字目から末尾まで切り取る
str[:-3]#最初から最後から3番目の文字まで切り取り
str[2]#3文字目を切り取る
str[-1]#最後から最初の文字を切り取る
str[:-1]#元の文字列の順序とは逆の文字列を作成
str[-3:-1]#最後から3番目と最後から1番目より前の文字を切り取る
str[-3:]#最後から3番目から最後まで切り取る
str[:-5:-3]#逆シーケンス切り取り
2、Python辞書の要素を取得する:
dict.get(x)存在しない値を読み込むと、Noneなどのデフォルト値が返されます.
dict[x]存在しない値を読み込むと、KeyError'x'などのエラーメッセージが実行されます.
printを使用してプログラムを詳細に表示できます.
3、Dijkstraアルゴリズムは改良することによって、K-最短経路アルゴリズムに変えることができる.(ネットでの実装参照)
4、最後に出力された文字列の3文字ごとに逆順序操作を行います.(コード改良)
5、経路文字列を分析し、重複する点を検出して回路を取り除く.(コード改良)
 
具体的な実装コードは(ネット上のソースコードを参照)
import heapq
import sys
 
class Graph:
	def __init__(self):
		self.vertices = {}
 
	def add_vertex(self, name, edges):
		self.vertices[name] = edges
	
	def get_shortest_path(self, startpoint, endpoint):
		# distances               startpoint    
		distances = {}
 
		#  startpoint              
		# eg:startpoint->B->D->E, previous[E]=D,previous[D]=B,  
		previous = {}
 
		#             startpoint         
		#             
		nodes = []
 
		# Dikstra        
		for vertex in self.vertices:
			if vertex == startpoint:
				#  startpoint        0
				distances[vertex] = 0
				heapq.heappush(nodes, [0, vertex])
			elif vertex in self.vertices[startpoint]:
				#   startpoint        startpoint             /  
				distances[vertex] = self.vertices[startpoint][vertex]
				heapq.heappush(nodes, [self.vertices[startpoint][vertex], vertex])
				previous[vertex] = startpoint
			else:
				#   startpoint           startpoint       sys.maxsize
				distances[vertex] = sys.maxsize
				heapq.heappush(nodes, [sys.maxsize, vertex])
				previous[vertex] = None
 
		while nodes:
			#             
			smallest = heapq.heappop(nodes)[1]
			if smallest == endpoint:
				shortest_path = []
				lenPath = distances[smallest]
				temp = smallest
				while temp != startpoint:
					shortest_path.append(temp)
					temp = previous[temp]
				#  startpoint     shortest_path 
				shortest_path.append(temp)
			if distances[smallest] == sys.maxsize:
				#       
				break
			#    smallest     ,         、    
			for neighbor in self.vertices[smallest]:
				dis = distances[smallest] + self.vertices[smallest][neighbor]
				if dis < distances[neighbor]:
					distances[neighbor] = dis
					#    smallest          
					previous[neighbor] = smallest
					for node in nodes:
						if node[1] == neighbor:
							#    smallest      startpoint   
							node[0] = dis
							break
					heapq.heapify(nodes)
		return distances, shortest_path, lenPath
 
	def getMinDistancesIncrement(self, inputList):
		inputList.sort()
		lenList = [v[0] for v in inputList]
		minValue = min(lenList)
		minValue_index = lenList.index(minValue)
		minPath = [v[1] for v in inputList][minValue_index]
		return minValue, minPath, minValue_index
 
	# def deleteCirclesWithEndpoint(self,inputList, endpoint):
	#	 '''
	#	                : endpoint->...->endpoint-->...
	#	 '''
	#	 pathsList = [v[1] for v in inputList]
	#	 for index, path in enumerate(pathsList):
	#		 if len(path) > 1 and path[-1] == endpoint:
	#			 inputList.pop(index)
	#	 return inputList
 
	def k_shortest_paths(self,start, finish, k = 3):
		'''
		:param start:    
		:param finish:   
		:param k:           
		:return:   K         
		           ,  get_shortest_path()                           
		'''
		distances, _, shortestPathLen = self.get_shortest_path(start, finish)
		num_shortest_path = 0
		paths = dict()
		distancesIncrementList = [[0, finish]]
		
		while num_shortest_path < k:
			path = []
			#distancesIncrementList = self.deleteCirclesWithEndpoint(distancesIncrementList,finish)
			minValue, minPath, minIndex = self.getMinDistancesIncrement(distancesIncrementList)
			#print('m:'+minPath)
			smallest_vertex = minPath[-3:]
			#print('v:'+smallest_vertex)
			distancesIncrementList.pop(minIndex)
 
			if smallest_vertex == start:
				path.append(minPath[::-1])
				num_shortest_path += 1
				# type(path) -> list,       key
				paths[path[0]] = minValue + shortestPathLen
				#print(paths)
				#     {path ; pathlen}      ,    {pathlen:path}
				#   key    ,         path      ,         
				# paths[minValue + shortestPathLen] = path
				continue
 
			for neighbor in self.vertices[smallest_vertex]:
				incrementValue = minPath
				increment = 0
				if neighbor == finish:
					#    deleteCirclesWithEndpoint()    
					continue
				if distances[smallest_vertex] == (distances[neighbor] + self.vertices[smallest_vertex][neighbor]):
					increment = minValue
				elif distances[smallest_vertex] < (distances[neighbor] + self.vertices[smallest_vertex][neighbor]):
					increment = minValue + distances[neighbor] + self.vertices[smallest_vertex][neighbor] - distances[smallest_vertex]
				elif distances[neighbor] == (distances[smallest_vertex] + self.vertices[smallest_vertex][neighbor]):
					increment = minValue + 2 * self.vertices[smallest_vertex][neighbor]
				distancesIncrementList.append([increment, incrementValue + neighbor])
		
		return paths
 
 
if __name__ == '__main__':
	g = Graph() #      
	g.add_vertex('a01', {'a02': 11.5, 'a70': 1.8, 'a74': 0.9})
	g.add_vertex('a02', {'a03': 3.6, 'a01': 11.5, 'a44': 6.5})
	g.add_vertex('a03', {'a02': 3.6, 'a04': 4.9, 'a14': 7.3, 'a15': 6.6, 'a58': 2.3})
	g.add_vertex('a04', {'a03': 4.9, 'a05': 6.3, 'a59': 1.1, 'a43': 2.1})
	g.add_vertex('a05', {'a04': 6.3, 'a06': 1.9, 'a17': 0.9, 'a27': 1.3})
	g.add_vertex('a06', {'a05': 1.9, 'a07': 3.1, 'a18': 1.2})
	g.add_vertex('a07', {'a06': 3.1, 'a08': 0.9, 'a19': 1.7, 'a34': 4.8})
	g.add_vertex('a08', {'a09': 3.3, 'a20': 1.9, 'a07': 0.9, 'a37': 5.7})
	g.add_vertex('a09', {'a10': 5.1, 'a84': 0.6, 'a08': 3.3, 'a87': 2})
	g.add_vertex('a10', {'a11': 5, 'a22': 0.8, 'a09': 5.1, 'a21': 3.2})
	g.add_vertex('a11', {'a12': 0.7, 'a10': 5, 'a22': 4.4})
	g.add_vertex('a12', {'a11': 0.7, 'a30': 6.6})
	g.add_vertex('a13', {'a14': 5.8, 'a81': 4, 'a45': 6})
	g.add_vertex('a14', {'a15': 0.8, 'a13': 5.8, 'a81': 8.2, 'a03': 7.3})
	g.add_vertex('a15', {'a55': 5.7, 'a81': 8.7, 'a14': 0.8, 'a03': 6.6})
	g.add_vertex('a16', {'a17': 2.4, 'a51': 3.9, 'a61': 1.9, 'a82': 3.1})
	g.add_vertex('a17', {'a18': 2.1, 'a28': 4.9, 'a16': 2.4, 'a05': 0.9})
	g.add_vertex('a18', {'a19': 2.5, 'a49': 3.9, 'a17': 2.1, 'a06': 1.2})
	g.add_vertex('a19', {'a20': 1, 'a36': 2.2, 'a48': 3.4, 'a18': 2.5, 'a07': 1.7})
	g.add_vertex('a20', {'a21': 4.1, 'a35': 1, 'a84': 4.2, 'a19': 1, 'a08': 1.9, 'a27': 6})
	g.add_vertex('a21', {'a10': 3.2, 'a20': 4.1, 'a84': 1.4})
	g.add_vertex('a22', {'a11': 4.4, 'a10': 0.8, 'a85': 4.8})
	g.add_vertex('a23', {'a24': 7.2, 'a31': 0.3, 'a80': 7.8,'a79': 2.4})
	g.add_vertex('a24', {'a25': 0.7, 'a32': 0.4, 'a23': 7.2})
	g.add_vertex('a25', {'a26': 1.3, 'a33': 0.5, 'a24': 0.7, 'a42': 8.3})
	g.add_vertex('a26', {'a27': 2.9, 'a34': 1.1, 'a83': 2.8, 'a25': 1.3})
	g.add_vertex('a27', {'a05': 1.3, 'a20': 6, 'a26': 2.9, 'a82': 2.1})
	g.add_vertex('a28', {'a29': 7.3, 'a17': 4.9, 'a36': 0.3})
	g.add_vertex('a29', {'a30': 1, 'a28': 7.3, 'a46': 6.6, 'a41': 2.1})
	g.add_vertex('a30', {'a12': 6.6, 'a29': 1})
	g.add_vertex('a31', {'a32': 8.1, 'a23': 0.3})
	g.add_vertex('a32', {'a33': 0.9, 'a37': 3.3, 'a24': 0.4, 'a31': 8.1})
	g.add_vertex('a33', {'a34': 0.8, 'a39': 3.9, 'a25': 0.5, 'a32': 0.9})
	g.add_vertex('a34', {'a07': 4.8, 'a26': 1.1, 'a33': 0.8})
	g.add_vertex('a35', {'a36': 0.6, 'a46': 1, 'a20': 1})
	g.add_vertex('a36', {'a40': 0.9, 'a28': 0.3, 'a19': 2.2, 'a35': 0.6})
	g.add_vertex('a37', {'a08': 5.7, 'a38': 0.6, 'a32': 3.3})
	g.add_vertex('a38', {'a39': 0.9, 'a37': 0.6})
	g.add_vertex('a39', {'a86': 2.9, 'a88': 1, 'a38': 0.9, 'a33': 3.9})
	g.add_vertex('a40', {'a46': 0.6, 'a41': 5.1, 'a36': 0.9, 'a47': 2.3})
	g.add_vertex('a41', {'a29': 2.1, 'a40': 5.1, 'a68': 8})
	g.add_vertex('a42', {'a82': 4.8, 'a25': 8.3, 'a43': 0.8, 'a78': 3.9})
	g.add_vertex('a43', {'a04': 2.1, 'a42': 0.8, 'a44':1 })
	g.add_vertex('a44', {'a43': 1, 'a59': 2.3, 'a02': 6.5, 'a79': 10.2})
	g.add_vertex('a45', {'a71': 3.3, 'a13': 6, 'a70': 5.9})
	g.add_vertex('a46', {'a29': 6.6, 'a35': 1, 'a40': 0.6})
	g.add_vertex('a47', {'a40': 2.3, 'a68': 8.5, 'a48': 1.3})
	g.add_vertex('a48', {'a47': 1.3, 'a67': 8.3, 'a19': 3.4, 'a49': 2.8})
	g.add_vertex('a49', {'a48': 2.8, 'a18': 3.9, 'a50': 2.7})
	g.add_vertex('a50', {'a49': 2.7, 'a53': 3.6, 'a51': 1.8})
	g.add_vertex('a51', {'a50': 1.8, 'a52': 0.7, 'a16': 3.9})
	g.add_vertex('a52', {'a51': 0.7, 'a53': 3.1, 'a63': 6.2})
	g.add_vertex('a53', {'a54': 0.7, 'a50': 3.6, 'a52': 3.1})
	g.add_vertex('a54', {'a66': 2.2, 'a53': 0.7, 'a65': 4.6})
	g.add_vertex('a55', {'a56': 1.5, 'a63': 3.5, 'a15': 5.7})
	g.add_vertex('a56', {'a60': 3, 'a62': 1.8, 'a55': 1.5, 'a57': 2})
	g.add_vertex('a57', {'a56': 2, 'a58': 1.9})
	g.add_vertex('a58', {'a59': 2.3, 'a57': 1.9, 'a71': 10.7, 'a03': 2.3})
	g.add_vertex('a59', {'a04': 1.1, 'a60': 3.6, 'a58': 2.3, 'a44': 2.3})
	g.add_vertex('a60', {'a61': 0.7, 'a56': 3, 'a59': 3.6})
	g.add_vertex('a61', {'a16': 1.9, 'a62': 3.6, 'a60': 0.7})
	g.add_vertex('a62', {'a63': 2.4, 'a56': 1.8, 'a61': 3.6})
	g.add_vertex('a63', {'a64': 3.4, 'a52': 6.2, 'a55': 3.5, 'a62': 2.4, 'a73': 11.7})
	g.add_vertex('a64', {'a65': 3.5, 'a69': 6.2, 'a63': 3.4})
	g.add_vertex('a65', {'a54': 4.6, 'a64': 3.5})
	g.add_vertex('a66', {'a67': 1.9, 'a54': 2.2, 'a69': 9})
	g.add_vertex('a67', {'a68': 2.5, 'a48': 8.3, 'a66': 1.9})
	g.add_vertex('a68', {'a41': 8, 'a47': 8.5, 'a67': 2.5})
	g.add_vertex('a69', {'a66': 9, 'a64': 6.2, 'a73': 6.8})
	g.add_vertex('a70', {'a45': 5.9, 'a01': 1.8})
	g.add_vertex('a71', {'a58': 10.7, 'a45': 3.3})
	g.add_vertex('a72', {'a73': 3.8, 'a81': 3.3})
	g.add_vertex('a73', {'a63': 11.7, 'a69': 6.8, 'a72': 3.8})
	g.add_vertex('a74', {'a75': 9.8, 'a78': 14.4, 'a01': 0.9})
	g.add_vertex('a75', {'a78': 11.5, 'a76': 9.7, 'a74': 9.8})
	g.add_vertex('a76', {'a77': 5.6, 'a80': 4.3, 'a75': 9.7})
	g.add_vertex('a77', {'a78': 8.3, 'a79': 6.3, 'a76': 5.6})
	g.add_vertex('a78', {'a42': 3.9, 'a74': 14.4, 'a75': 11.5, 'a77': 8.3})
	g.add_vertex('a79', {'a23': 2.4, 'a44': 10.2, 'a77': 6.3})
	g.add_vertex('a80', {'a23': 7.8, 'a76': 4.3})
	g.add_vertex('a81', {'a72': 3.3, 'a14': 8.2, 'a13': 4, 'a15': 8.7})
	g.add_vertex('a82', {'a27': 2.1, 'a16': 3.1, 'a42': 4.8, 'a83': 2.5})
	g.add_vertex('a83', {'a82': 2.5, 'a26':2.8})
	g.add_vertex('a84', {'a21': 1.4, 'a85': 5.1, 'a09': 0.6, 'a20': 4.2})
	g.add_vertex('a85', {'a22': 4.8, 'a84': 5.1, 'a90': 4.1})
	g.add_vertex('a86', {'a89': 2.6, 'a87': 2.6, 'a39': 2.9})
	g.add_vertex('a87', {'a09': 2, 'a90': 4.8, 'a86': 2.6})
	g.add_vertex('a88', {'a89': 3.3, 'a39': 1})
	g.add_vertex('a89', {'a90': 3.7, 'a86': 2.6, 'a88': 3.3})
	g.add_vertex('a90', {'a85': 4.1, 'a87': 4.8, 'a89': 3.7})
	start = 'a01'
	end = 'a90'
	k = 250 #   K 
	
	distances, shortestPath, shortestPathLen = g.get_shortest_path(start, end)
	#print('{}->{}      :{},     :{}'.format(start, end, shortestPath, shortestPathLen))
	paths = g.k_shortest_paths(start, end, k)
	#print(paths)
	print('
{}-->{} {}- :'.format(start, end, k)) index = 1 for path, length in paths.items(): # 3 i = 0 j = 2 realPath = "" while j <= len(path): temp1 = path[i:j+1] #print(temp1[::-1]) realPath += temp1[::-1] i += 3 j += 3 # 3 k1 = 0 k2 = 3 flag = 0; while k1 <= len(realPath)-3: temp2 = realPath[k1:k1+3] # #print("2:"+temp2) while k2 <= len(realPath)-3: temp3 = realPath[k2:k2+3] #print("3:"+temp3) if temp2 == temp3: flag = 1 break k2 += 3 k1 += 3 k2 = k1 + 3 # if flag==0: print('{}:{} :{}'.format(index, realPath, length)) index += 1

 
参考記事:
https://blog.csdn.net/sxt1001/article/details/80956564
https://blog.csdn.net/qingzhuyuxian/article/details/79882088