Добавил:
Кафедра ВТ Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

lab2

.py
Скачиваний:
21
Добавлен:
18.02.2023
Размер:
12.12 Кб
Скачать
#  4, 8, 1,       1, 2, 3,
#  0, 3, 6,  ->   8, 0, 4,
#  2, 7, 5        7, 6, 5

import sys
import enum
import heapq
import psutil
import os
from time import process_time

START_TIME = 0  # время запуска программы
TIME_STOP = 0 # время окончания программы
TREE = None  # дерево решения
DEBUG = True
''' Если True, то каждый шаг придётся нажимать enter'''
H1_H2 = True
''' Выбор эвристической функции'''

class Tree:
    ''' Класс представления дерева '''
    # Все его методы - статические, т.к. дерево - единственное
    __nodes = []
    '''Все узлы'''
    __hashes = {}

    #def __init__(self):
        # node = Node(get_initial_state(), None, None, 0, 0)
        # self.__nodes = {0: [node]}

    def add_node(self, new_node: "Node"):
        ''' Добавить узел в дерево '''
        # если состояние повторяется, узел не добавляется
        if (not self.hasState(new_node.current_state)):
            self.__nodes.append(new_node)
            self.__hashes[state_hash(new_node.current_state)] = new_node

    def getNode(self, node: int) -> list["Node"]:
        '''Получить список узлов по глубине'''
        return list(self.__nodes[node])

    def getNodeByState(self, state: list) -> "Node":
        return self.__hashes[state_hash(state)]
    
    def hasState(self, state: list) -> bool:
        return state_hash(state) in self.__hashes

    def print_node(self, node: "Node"):
        ''' Вывод узла на экран '''
        parent_id: int = None
        if(node.parent_node):
            parent_id = node.parent_node.node_id
        node_prev_action: str = None
        if(node.previous_action):
            node_prev_action = node.previous_action.name
        print(f"id = {node.node_id}, parent_id = {parent_id}, " +
            f"action = {node_prev_action}, " +
            f"cost = {node.path_cost}, current_state: ")
        self.print_state(node.current_state)
        print("")

    def print_state(self, state: list):
        ''' Вывод состояния в три ряда'''
        for i in range(9):
            if (i % 3 == 0 and i != 0):
                 print("")
            print(state[i] if state[i] != 0 else " ", end=" ")

    def print_path(self, node: "Node", isReversed = False):
        ''' Вывод пути на экран '''
        path = []
        current_node = node

        while current_node.parent_node:
            path.append(current_node)
            current_node = current_node.parent_node
        path.append(current_node)
        if (isReversed):
            path = path[::-1]
        for path_node in path:
            self.print_node(path_node)
            print("\t^\n\t|\n")


class Action(enum.Enum):
    ''' Перечисление действий над полем '''
    UP = 1
    DOWN = 2
    RIGHT = 3
    LEFT = 4

class Node:
    ''' Класс представления узла '''
    current_state: list = None
    ''' Начальное состояние'''
    parent_node: "Node" = None
    ''' Указатель на родительский узел'''
    previous_action: Action = None
    '''Действие, которое было применено к родительскому узлу для формирования данного узла'''
    path_cost: int = 0
    '''Стоимость пути от начального состояния до данного узла g(n)'''
    #depth: int = 0
    '''Количество этапов пути от начального состояния (глубина)'''
    node_id: int = 0
    nodes_count = 0

    def __init__(self, state: list, parent: "Node", action: Action, cost: int):
        self.current_state = state
        self.parent_node = parent
        self.previous_action = action
        self.path_cost = cost
        #self.depth = depth
        self.node_id = Node.nodes_count
        Node.nodes_count += 1

    @classmethod
    def get_nodes_count(cls) -> int:
        ''' Статический метод класса, возвращающий количество узлов '''
        return cls.nodes_count + 1

def get_initial_state() -> list:
    ''' Получение начального состояния игры (Вариант 7) '''
    return [4, 8, 1,
            0, 3, 6,
            2, 7, 5 ]

def get_finish_state() -> list:
    ''' Получение конечного состояния игры (Вариант 7) '''
    return [1, 2, 3,
            8, 0, 4,
            7, 6, 5 ]

# O(1)
def check_final(current_state: list) -> bool:
    ''' Проверка, является ли данное состояние конечным '''
    return current_state == get_finish_state()

# O(1)
def state_hash(state: list) -> int:
    ''' Хэширование состояния (листа)'''
    hash = 7
    for i in state:
        hash = 31*hash + i
    return hash

# O(1)
def heuristics(node: "Node") -> int:
    if(not H1_H2):
        cur_value = node.path_cost + h1(node.current_state)
    else:
        cur_value = node.path_cost + h2(node.current_state)
    return cur_value

# O(1)
def h1(state: list) -> int:
    '''Возвращает кол-во фишек не на своем месте'''
    state_final = get_finish_state()
    res = 0
    for i in range(9):
        if(state_final[i] != state[i]):
            res+=1
    return res

# O(1)
def h2(state: list) -> int:#метод Манхеттена
    '''Возвращает сумму Манхеттенских расстояний'''
    state_final = get_finish_state()
    res = 0
    for i in range(9):
        state_x, state_y = i // 3, i % 3
        where = state_final.index(state[i])
        final_x, final_y = where // 3, where % 3
        res += abs(final_x-state_x) + abs(final_y-state_y)
    return res

def print_info(iterations: int, time: float):
    print(f"Итого узлов: {Node.get_nodes_count()}")
    print(f"Итого итераций: {iterations}")
    print(f"Потраченное время процессора: {time*1000} миллисекунд")
    print(f"Памяти использовано: {psutil.Process(os.getpid()).memory_info().rss} байтов")

# O(1)
def state_swap(new_states: dict, current_state: list, i: int, j: int, action: Action):
    state = list(current_state)
    state[i], state[j] = state[j], state[i]
    new_states[action] = state

# O(1)
def get_new_states(current_state: list) -> dict[Action, list[Node]]:
    ''' Получение новых состояний поля '''
    #В словаре ключ - действие для получения состояния, значение - само состояние
    new_states = {}
    pos = current_state.index(0)
    # up
    if pos not in (0, 1, 2):
        state_swap(new_states, current_state, pos, pos-3, Action.UP)
    # down
    if pos not in (6, 7, 8):
        state_swap(new_states, current_state, pos, pos+3, Action.DOWN)
    # right
    if pos not in (2, 5, 8):
        state_swap(new_states, current_state, pos, pos+1, Action.RIGHT)
    # left
    if pos not in (0, 3, 6):
        state_swap(new_states, current_state, pos, pos-1, Action.LEFT)
    return new_states

# O(1)
def get_neighbours(node: Node) -> list[Node]:
    new_states_dict = get_new_states(node.current_state)
    neighbours = []
    for new_state_action in new_states_dict:
        new_state = new_states_dict[new_state_action]
        if (TREE.hasState(new_state)):
            neighbours.append(TREE.getNodeByState(new_state))
        else:
            new_node = Node(new_state, node, new_state_action, node.path_cost+1)
            neighbours.append(new_node)
            TREE.add_node(new_node)
    return neighbours

# O(C^N), C -  константа, N - глубина найденного узла
def A_star():
    open_list = set() #список граничащих узлов
    # следующая ячейка выбирается согласно эвристической функции
    close_list = set() # список узлов, больше не учавствующие в вычислениях
    heap = [] # представление кучи
    Found = False

    current_node = Node(get_initial_state(), None, None, 0)
    TREE.add_node(current_node)
    close_list.add(current_node)
    neighbours = get_neighbours(current_node)
    for neighbour in neighbours:
        open_list.add(neighbour.node_id)
        neighbour_h = heuristics(neighbour)
        heapq.heappush(heap, (neighbour_h, neighbour.node_id, neighbour))
    iteration_count = 0
    step_i = 0

    while(not Found):
        iteration_count+=1
        heap_lowest = heapq.heappop(heap) #возвращает и удаляет наименьший элемент из кучи
        current_node: Node = heap_lowest[2] #берется 3й элемент, т.к. см. heappush выше
        open_list.remove(current_node.node_id)
        close_list.add(current_node.node_id)
        if(DEBUG):
            print(f"Длина открытого списка = {len(open_list)}")
            print(f"Длина закрытого списка = {len(close_list)}")
            print(f"Был выбран узел из-за минимального значения функции f={heap_lowest[0]}")
            print("Текущий узел : ")
            TREE.print_node(current_node)
        if (check_final(current_node.current_state)):
            if(DEBUG):
                print("Ответ найден!")
                TREE.print_node(current_node)
            Found = True
            TIME_STOP = process_time()
            TREE.print_path(current_node)
            print_info(iteration_count, TIME_STOP - START_TIME)
            break
        neighbours = get_neighbours(current_node)
        if(DEBUG):
            print("Соседи текущего узла:")
            for neighbour in neighbours:
                if(neighbour.node_id in close_list):
                    print("Этот сосед находится в закрытом списке и не будет рассматриваться")
                elif(neighbour.node_id in open_list):
                    print("Этот сосед находится в открытом списке и при необходимости будет для него будет пересчитана функция")
                else:
                    print("Этот сосед новый и ранее не рассматривался. Он будет добавлен в список открытых")
                TREE.print_node(neighbour)
                print(f"Функция f = {heuristics(neighbour)}")
        for neighbour in neighbours:
            if (neighbour.node_id in open_list):
                old_g = neighbour.path_cost
                new_g = current_node.path_cost + 1
                if (new_g < old_g):
                    neighbour.path_cost = new_g
                    neighbour.parent_node = current_node
            else:
                if(neighbour.node_id not in close_list):
                    open_list.add(neighbour.node_id)
                    neighbor_i_h = heuristics(neighbour)
                    heapq.heappush(heap, (neighbor_i_h, neighbour.node_id, neighbour))
        if(DEBUG):
            print(f"Текущий шаг: {step_i}. Нажмите Enter... ")
            input()
            print("\n")
        step_i += 1

if __name__ == '__main__':
    START_TIME = process_time()
    TREE = Tree()
    A_star()
Соседние файлы в предмете Введение в искусственный интеллект