pythonはA*道探しアルゴリズムを実現します。


A*アルゴリズムの概要
A*アルゴリズムは二つのデータ構造を維持する必要があります。OPENセットとCLOEDセットです。OPENセットは、検索済みの検出待ちノードをすべて含む。初期状態では、OPENセットには1つの要素しか含まれていません。開始ノードです。CLOSDセットは、検出されたノードを含む。初期状態はCLOED集が空です。各ノードはまた、親ノードを指すポインタを含み、追跡関係を決定する。
A*アルゴリズムは、検索したノードごとにG+Hの和値Fを計算する。
  • F=G+H
  • G:開始ノードから現在ノードへの移動量である。開始ノードから隣接ノードへの移動量は1であると仮定すると、この値は開始点から遠くなるほど大きくなる。
  • H:現在のノードから対象ノードへの移動量推定値である。
  • もし4近傍への移動が許可されれば、マンハッタン距離を使用する。
  • は、8近傍への移動を許可する場合、対角線距離を使用する。
  • アルゴリズムは、ターゲットノードに到達するまで次のステップを繰り返します。
    1は、OPENから最適ノードn(すなわち、F値が一番小さいノード)をまとめて検出する。
    2ノードnをOPENから集中的に除去し、その後CLOSDセットに追加する。
    3 nがターゲットノードである場合、アルゴリズムは終了する。
    4そうでなければ、ノードnのすべての隣ノードn'を追加することが試みられる。
  • 隣ノードは、CLOSD集中において、検出されたことを表し、追加する必要がない。
  • 隣ノードはOPENに集中している:
  • 、再計算されたG値が隣のノードに保存されているG値より小さい場合、この隣のノードのG値とF値、および親ノードを更新する必要がある。
  • そうでなければ、操作をしません。
  • そうでなければ、隣接ノードをOPENセットに参加し、その親ノードがnであることを設定し、そのG値とF値を設定する。
  • スタートノードからターゲットノードまでが実際に接続されていない場合、スタートノードからターゲットノードに移動できない場合、アルゴリズムは、第1ステップで取得されたノードnが空であると判断し、終了することに注意する必要がある。
    キーコードの紹介
    基本情報を保存する地図類
    地図の種類はランダムに1つの道を探すアルゴリズムの仕事の基礎の地図の情報を生成するために用います。
    まずmapクラスを作成し、初期化パラメータは地図の長さと幅を設定し、地図情報を保存する二次元データmapの値を0とし、値は0として当該ノードに移動できることを示します。
    
    class Map():
    	def __init__(self, width, height):
    		self.width = width
    		self.height = height
    		self.map = [[0 for x in range(self.width)] for y in range(self.height)]
    
    mapクラスにノードを介して作成できない関数を追加し、ノード値1はノードに移動できないことを示します。
    
    	def createBlock(self, block_num):
    		for i in range(block_num):
    			x, y = (randint(0, self.width-1), randint(0, self.height-1))
    			self.map[y][x] = 1
    
    mapクラスに地図を表示する関数を追加すると、ここではすべてのノードの値を簡単にプリントアウトしただけで、値が0または1という意味で既に説明されています。後ろに道を探すアルゴリズムの結果が表示されると、値2に使用され、スタートノードからターゲットノードまでの経路を表します。
    
    	def showMap(self):
    		print("+" * (3 * self.width + 2))
    		for row in self.map:
    			s = '+'
    			for entry in row:
    				s += ' ' + str(entry) + ' '
    			s += '+'
    			print(s)
    		print("+" * (3 * self.width + 2))
    
    ランダム取得可能なノードの関数を追加します。
    
    	def generatePos(self, rangeX, rangeY):
    		x, y = (randint(rangeX[0], rangeX[1]), randint(rangeY[0], rangeY[1]))
    		while self.map[y][x] == 1:
    			x, y = (randint(rangeX[0], rangeX[1]), randint(rangeY[0], rangeY[1]))
    		return (x , y)
    
    検索したノードクラス
    OPENセットに追加されるノードを検索するごとに、イベントの位置情報(x,y)が保存されている次のノード類が作成され、算出されたG値とF値と、そのノードの親ノード(pre_u)が格納されます。ベントリー
    
    class SearchEntry():
    	def __init__(self, x, y, g_cost, f_cost=0, pre_entry=None):
    		self.x = x
    		self.y = y
    		# cost move form start entry to this entry
    		self.g_cost = g_cost
    		self.f_cost = f_cost
    		self.pre_entry = pre_entry
    	
    	def getPos(self):
    		return (self.x, self.y)
    
    アルゴリズムの主関数紹介
    以下は、上のアルゴリズムの主なループで紹介されたコード実装であり、OPENセットとCLOSDセットのデータ構造は辞書を使用しており、一般的には、ノードの追加と削除の時間の複雑さはO(1)であり、遍歴時間の複雑さはO(n)であり、nは辞書の対象数である。
    
    def AStarSearch(map, source, dest):
    	...
    	openlist = {}
    	closedlist = {}
    	location = SearchEntry(source[0], source[1], 0.0)
    	dest = SearchEntry(dest[0], dest[1], 0.0)
    	openlist[source] = location
    	while True:
    		location = getFastPosition(openlist)
    		if location is None:
    			# not found valid path
    			print("can't find valid path")
    			break;
    		
    		if location.x == dest.x and location.y == dest.y:
    			break
    		
    		closedlist[location.getPos()] = location
    		openlist.pop(location.getPos())
    		addAdjacentPositions(map, location, dest, openlist, closedlist)
    	
    	#mark the found path at the map
    	while location is not None:
    		map.map[location.y][location.x] = 2
    		location = location.pre_entry
    
    アルゴリズムの主循環の実現によって、使用する関数を説明します。
    以下の関数はOPEN集中からF値が一番小さいノードを取得します。OPEN集会が空であればNoneに戻ります。
    
    	# find a least cost position in openlist, return None if openlist is empty
    	def getFastPosition(openlist):
    		fast = None
    		for entry in openlist.values():
    			if fast is None:
    				fast = entry
    			elif fast.f_cost > entry.f_cost:
    				fast = entry
    		return fast
    
    addAdjacentPositions関数対応アルゴリズムの主関数サイクル紹介における試みは、ノードnのすべての隣接ノードn'を追加する。
    
    	# add available adjacent positions
    	def addAdjacentPositions(map, location, dest, openlist, closedlist):
    		poslist = getPositions(map, location)
    		for pos in poslist:
    			# if position is already in closedlist, do nothing
    			if isInList(closedlist, pos) is None:
    				findEntry = isInList(openlist, pos)
    				h_cost = calHeuristic(pos, dest)
    				g_cost = location.g_cost + getMoveCost(location, pos)
    				if findEntry is None :
    					# if position is not in openlist, add it to openlist
    					openlist[pos] = SearchEntry(pos[0], pos[1], g_cost, g_cost+h_cost, location)
    				elif findEntry.g_cost > g_cost:
    					# if position is in openlist and cost is larger than current one,
    					# then update cost and previous position
    					findEntry.g_cost = g_cost
    					findEntry.f_cost = g_cost + h_cost
    					findEntry.pre_entry = location
    
    get Positions関数は、移動可能なすべてのノードを取得し、ここでは2つの移動方法を提供する。
  • は、上下、左、右の4近傍の移動を許可する
  • は、上下、左、右、左上、右上、左下、右下の8近傍の移動を許可する
  • 
    	def getNewPosition(map, locatioin, offset):
    		x,y = (location.x + offset[0], location.y + offset[1])
    		if x < 0 or x >= map.width or y < 0 or y >= map.height or map.map[y][x] == 1:
    			return None
    		return (x, y)
    		
    	def getPositions(map, location):
    		# use four ways or eight ways to move
    		offsets = [(-1,0), (0, -1), (1, 0), (0, 1)]
    		#offsets = [(-1,0), (0, -1), (1, 0), (0, 1), (-1,-1), (1, -1), (-1, 1), (1, 1)]
    		poslist = []
    		for offset in offsets:
    			pos = getNewPosition(map, location, offset)
    			if pos is not None:			
    				poslist.append(pos)
    		return poslist
    
    isInList関数は、ノードがOPENセットまたはCLOSDセットにあるかどうかを判断する。
    
    	# check if the position is in list
    	def isInList(list, pos):
    		if pos in list:
    			return list[pos]
    		return None
    
    calHeuristic関数はマンハッタン距離を簡単に使いました。この後は最適化できます。
    get MoveCost関数は、斜め移動かどうかによって消費を計算します。
    
    	# imporve the heuristic distance more precisely in future
    	def calHeuristic(pos, dest):
    		return abs(dest.x - pos[0]) + abs(dest.y - pos[1])
    		
    	def getMoveCost(location, pos):
    		if location.x != pos[0] and location.y != pos[1]:
    			return 1.4
    		else:
    			return 1
    
    コードの初期化
    地図の長さ、幅、および非移動ノードの数を調整することができる。
    開始ノードおよび対象ノードの取得範囲を調整することができる。
    
    WIDTH = 10
    HEIGHT = 10
    BLOCK_NUM = 15
    map = Map(WIDTH, HEIGHT)
    map.createBlock(BLOCK_NUM)
    map.showMap()
    
    source = map.generatePos((0,WIDTH//3),(0,HEIGHT//3))
    dest = map.generatePos((WIDTH//2,WIDTH-1),(HEIGHT//2,HEIGHT-1))
    print("source:", source)
    print("dest:", dest)
    AStarSearch(map, source, dest)
    map.showMap()
    
    実行される効果図は、最初にランダムに生成された地図を示し、値が1のノードはノードに移動できないことを示している。
    第2の図の値が2のノードは、発見された経路を示す。
    在这里插入图片描述
    完全コード
    python 3.7を使ってコンパイルします。
    
    from random import randint
    
    class SearchEntry():
    	def __init__(self, x, y, g_cost, f_cost=0, pre_entry=None):
    		self.x = x
    		self.y = y
    		# cost move form start entry to this entry
    		self.g_cost = g_cost
    		self.f_cost = f_cost
    		self.pre_entry = pre_entry
    	
    	def getPos(self):
    		return (self.x, self.y)
    
    class Map():
    	def __init__(self, width, height):
    		self.width = width
    		self.height = height
    		self.map = [[0 for x in range(self.width)] for y in range(self.height)]
    	
    	def createBlock(self, block_num):
    		for i in range(block_num):
    			x, y = (randint(0, self.width-1), randint(0, self.height-1))
    			self.map[y][x] = 1
    	
    	def generatePos(self, rangeX, rangeY):
    		x, y = (randint(rangeX[0], rangeX[1]), randint(rangeY[0], rangeY[1]))
    		while self.map[y][x] == 1:
    			x, y = (randint(rangeX[0], rangeX[1]), randint(rangeY[0], rangeY[1]))
    		return (x , y)
    
    	def showMap(self):
    		print("+" * (3 * self.width + 2))
    
    		for row in self.map:
    			s = '+'
    			for entry in row:
    				s += ' ' + str(entry) + ' '
    			s += '+'
    			print(s)
    
    		print("+" * (3 * self.width + 2))
    	
    
    def AStarSearch(map, source, dest):
    	def getNewPosition(map, locatioin, offset):
    		x,y = (location.x + offset[0], location.y + offset[1])
    		if x < 0 or x >= map.width or y < 0 or y >= map.height or map.map[y][x] == 1:
    			return None
    		return (x, y)
    		
    	def getPositions(map, location):
    		# use four ways or eight ways to move
    		offsets = [(-1,0), (0, -1), (1, 0), (0, 1)]
    		#offsets = [(-1,0), (0, -1), (1, 0), (0, 1), (-1,-1), (1, -1), (-1, 1), (1, 1)]
    		poslist = []
    		for offset in offsets:
    			pos = getNewPosition(map, location, offset)
    			if pos is not None:			
    				poslist.append(pos)
    		return poslist
    	
    	# imporve the heuristic distance more precisely in future
    	def calHeuristic(pos, dest):
    		return abs(dest.x - pos[0]) + abs(dest.y - pos[1])
    		
    	def getMoveCost(location, pos):
    		if location.x != pos[0] and location.y != pos[1]:
    			return 1.4
    		else:
    			return 1
    
    	# check if the position is in list
    	def isInList(list, pos):
    		if pos in list:
    			return list[pos]
    		return None
    	
    	# add available adjacent positions
    	def addAdjacentPositions(map, location, dest, openlist, closedlist):
    		poslist = getPositions(map, location)
    		for pos in poslist:
    			# if position is already in closedlist, do nothing
    			if isInList(closedlist, pos) is None:
    				findEntry = isInList(openlist, pos)
    				h_cost = calHeuristic(pos, dest)
    				g_cost = location.g_cost + getMoveCost(location, pos)
    				if findEntry is None :
    					# if position is not in openlist, add it to openlist
    					openlist[pos] = SearchEntry(pos[0], pos[1], g_cost, g_cost+h_cost, location)
    				elif findEntry.g_cost > g_cost:
    					# if position is in openlist and cost is larger than current one,
    					# then update cost and previous position
    					findEntry.g_cost = g_cost
    					findEntry.f_cost = g_cost + h_cost
    					findEntry.pre_entry = location
    	
    	# find a least cost position in openlist, return None if openlist is empty
    	def getFastPosition(openlist):
    		fast = None
    		for entry in openlist.values():
    			if fast is None:
    				fast = entry
    			elif fast.f_cost > entry.f_cost:
    				fast = entry
    		return fast
    
    	openlist = {}
    	closedlist = {}
    	location = SearchEntry(source[0], source[1], 0.0)
    	dest = SearchEntry(dest[0], dest[1], 0.0)
    	openlist[source] = location
    	while True:
    		location = getFastPosition(openlist)
    		if location is None:
    			# not found valid path
    			print("can't find valid path")
    			break;
    		
    		if location.x == dest.x and location.y == dest.y:
    			break
    		
    		closedlist[location.getPos()] = location
    		openlist.pop(location.getPos())
    		addAdjacentPositions(map, location, dest, openlist, closedlist)
    		
    	#mark the found path at the map
    	while location is not None:
    		map.map[location.y][location.x] = 2
    		location = location.pre_entry	
    
    	
    WIDTH = 10
    HEIGHT = 10
    BLOCK_NUM = 15
    map = Map(WIDTH, HEIGHT)
    map.createBlock(BLOCK_NUM)
    map.showMap()
    
    source = map.generatePos((0,WIDTH//3),(0,HEIGHT//3))
    dest = map.generatePos((WIDTH//2,WIDTH-1),(HEIGHT//2,HEIGHT-1))
    print("source:", source)
    print("dest:", dest)
    AStarSearch(map, source, dest)
    map.showMap()
    
    ここで、pythonについてA*道探しアルゴリズムを実現する文章を紹介します。もっと関連のあるpython A*道探しアルゴリズムの内容は以前の文章を検索してください。または次の関連記事を引き続きご覧ください。これからもよろしくお願いします。