ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python 자료구조 #큐3] 우선순위 큐, 미로 찾기
    Programming 기초/Python 2023. 5. 15. 14:33

    * 우선순위 큐(priority queue)

    • 삭제 연산에서 어떤 요소가 먼저 삭제되는가에 따라 최대 우선순위 큐와 최소 우선순위 큐로 나누어지지만, 특별한 언급이 없으면 우선순위 큐는 가장 높은 우선순위의 요소가 먼저 삭제되는 최대 우선순위 큐를 의미한다.
    • 실제로 가장 효율적인 우선순위 큐의 구현 방법은 트리 구조를 사용하는 힙(heap)이다. (여기서는 리스트를 이용해서 구현할 것임.)
    • 단순화를 위해 데이터 자체가 우선순위를 나타낸다고 가정한다. 일반적으로는 (데이터, 우선순위) 형식의 튜플 구조가 바람직하다.
    from Stack import*	# 이전 포스팅에서 만든 stack.py 모듈을 가져왔다.
    
    class PriorityQueue :	# 우선순위 큐 클래스 생성
        def __init__( self ):					
            self.items = []						
    
        def isEmpty( self ):					
            return len( self.items ) == 0
        def size( self ): return len(self.items)
        def clear( self ): self.items = []		
    
        def enqueue( self, item ):		# 삽입 연산
            self.items.append( item )	# 리스트의 맨 뒤에 삽입(시간복잡도 O(1))
    
        def findMaxIndex( self ):		# 최대 우선순위 항목의 인덱스 반환
            if self.isEmpty(): return None
            else:
                highest = 0						
                for i in range(1, self.size()) :
                    if self.items[i] > self.items[highest] :
                        highest = i	
                return highest		
    
    
        def dequeue( self ):		# 삭제 연산
            highest = self.findMaxIndex()		
            if highest is not None :
                return self.items.pop(highest)	
    
        def peek( self ):				#peek 연산
            highest = self.findMaxIndex()	
            if highest is not None :
                return self.items[highest]

    우선순위 큐 적용 예시:

    q = PriorityQueue()
    q.enqueue( 34 )
    q.enqueue( 18 )
    q.enqueue( 27 )
    q.enqueue( 45 )
    q.enqueue( 15 )
    
    print("PQueue:", q.items)
    while not q.isEmpty() :
        print("Max Priority = ", q.dequeue() )
    -------------------------------------------------------
    PQueue: [34, 18, 27, 45, 15]
    Max Priority =  45
    Max Priority =  34
    Max Priority =  27
    Max Priority =  18
    Max Priority =  15

     

    * 우선순위 큐의 응용 : 전략적인 미로 탐색

    • 우선순위 큐에 미로에서의 현위치를 저장한다. 큐는 [x,y,distance] 위치 정보를 가지는 리스트들의 모음이다. 고로 최고 우선순위 항목을 찾기 위해서는 distance의 값을 비교해야하고, 인덱스[_][2]로 표시된다.
    • 위에 PriorityQueue 클래스에 있는 findMaxIndex 함수를 아래와 같이 변경해준다.
        def findMaxIndex(self):
            if self.isEmpty():
                return None
            else:
                highest = 0
                for i in range(1, self.size()):
                    if self.items[i][2] > self.items[highest][2]:	# [i][2]는 distance값을 나타낸다. [x, y, distance]
                        highest = i
                return highest
    from PriorityQueue import * # 앞서 만들어 놓은 우선순위 큐 모듈을 불러온다.
    
    import math # 내장라이브러리 math 모듈을 불러온다.
    
    
    def dist(x, y):
        (dx, dy) = (ox - x, oy - y)
        return math.sqrt(dx * dx + dy * dy)
    
    
    def isValidPos(x, y):
        if x < 0 or y < 0 or x >= MAZE_SIZE or y >= MAZE_SIZE:
            return False
        else:
            return map[y][x] == "0" or map[y][x] == "x"	# 위치(x, y)는 리스트의 요소인 튜플을 지정한다.
    
    
    def MySmartSearch():
        q = PriorityQueue()
        q.enqueue((0, 1, -dist(0, 1)))	# 출발 위치에 대한 튜플을 우선순위 큐에 저장한다.
        print("PQueue: ")
    
        while not q.isEmpty():
            here = q.dequeue()
            print(here[0:2], end="->")	# 현위치 [X, Y, distance] 표시
            x, y, _ = here	# 언더바도 변수 이름이다. 변수 저장 형식을 맞추기 위해 distance를 변수(언더바)에 저장한 것일뿐이다.
            if map[y][x] == "x":	# 여기서 x는 출구를 뜻하는 문자열이다.
                return True
            else:
                map[y][x] = "."	# 지나온 자리 표시
                if isValidPos(x, y - 1):	# 상 방향 검사
                    q.enqueue((x, y - 1, -dist(x, y - 1)))
                if isValidPos(x, y + 1):	# 하 방향 검사
                    q.enqueue((x, y + 1, -dist(x, y + 1)))
                if isValidPos(x - 1, y):	# 좌 방향 검사
                    q.enqueue((x - 1, y, -dist(x - 1, y)))
                if isValidPos(x + 1, y):	# 우 방향 검사
                    q.enqueue((x + 1, y, -dist(x + 1, y)))
            print("우선순위큐: ", q.items)
        return False

    미로탐색 예시:

    (ox, oy) = (5, 4)  # 출구 위치
    map = [
        ["1", "1", "1", "1", "1", "1"],  # 미로
        ["e", "0", "1", "0", "0", "1"],
        ["1", "0", "0", "0", "1", "1"],
        ["1", "0", "1", "0", "1", "1"],
        ["1", "0", "1", "0", "0", "x"],
        ["1", "1", "1", "1", "1", "1"],
    ]
    MAZE_SIZE = 6  # 정사각형 미로 크기
    result = MySmartSearch()
    if result:
        print(" --> 미로탐색 성공")
    else:
        print(" --> 미로탐색 실패")
    ----------------------------------------------------------------------------------------------
    PQueue: 
    (0, 1)->우선순위큐:  [(1, 1, -5.0)]
    (1, 1)->우선순위큐:  [(1, 2, -4.47213595499958)]
    (1, 2)->우선순위큐:  [(1, 3, -4.123105625617661), (2, 2, -3.605551275463989)]
    (2, 2)->우선순위큐:  [(1, 3, -4.123105625617661), (3, 2, -2.8284271247461903)]
    (3, 2)->우선순위큐:  [(1, 3, -4.123105625617661), (3, 1, -3.605551275463989), (3, 3, -2.23606797749979)]
    (3, 3)->우선순위큐:  [(1, 3, -4.123105625617661), (3, 1, -3.605551275463989), (3, 4, -2.0)]
    (3, 4)->우선순위큐:  [(1, 3, -4.123105625617661), (3, 1, -3.605551275463989), (4, 4, -1.0)]
    (4, 4)->우선순위큐:  [(1, 3, -4.123105625617661), (3, 1, -3.605551275463989), (5, 4, -0.0)]
    (5, 4)-> --> 미로탐색 성공

    아래 그림은 탐색 순서와 현재 위치에 대한 튜플(x,y) 그리고 리스트의 인덱스를 비교하는 표이다.

    map[y][x] [y][0] [y][1] [y][2] [y][3] [y][4] [y][5]
    map[0]            
    map[1] 1(e) 2        
    map[2]   3
    (1,2)
    4 5
    (3,2)
       
    map[3]       6    
    map[4]       7 8 9(x)
    map[5]            

    ※ 파이썬에서 언더 바의 사용

    https://blog.naver.com/jkg57/222222700891

    댓글

Designed by Tistory.