Способы нахождения цели. Роль случайности

от автора

1. Введение

Поиск может быть как сложным, так и простым. Когда не известна (или только частично известна) как сама цель, так и способ её достижения, случайность важна

Целью исследования статьи будет сравнение способов нахождения цели как подвижной (жёлтый объект), так и неподвижной.

Эти способы:

  • Случайный поиск (красный объект)
  • Случайный поиск с памятью (синий объект)
  • Случайный поиск с памятью и иерархией (зелёный объект)
  • Поиск первого маршрута (фиолетовый объект)
  • Поиск короткого маршрута (коричневый объект)

На рис.1 эти объекты показаны. Полностью код программы выложен на github


2. Основная часть

2.1. Класс P – случайный поиск

Конструктор инициализирует атрибуты и переменные класса: главное окно, цвет, координату по y и x, счётчик, словарь посещённых, цель, словарь иерархии, словарь соседей. Метод init_3_dict заполняет три словаря. Метод show закрашивает клетку в нужный цвет. Метод update обновляет объект. Метод top_show создаёт окно верхнего уровня и показывает сколько шагов сделано до цели.

class P():     def __init__(self, root, color, node, target, maps, ban):         self.root = root         self.color = color         self.y = node[0]         self.x = node[1]         P.target = target         self.count = 0                  self.visit = {}         P.hierarchy = {}         P.neighbor = {}                  self.init_3_dict(maps, ban)

В методе init_3_dict объявляется функция is_en, которая проверяет, чтобы координата не была забанина и не выходила за рамки карты.

def init_3_dict(self, maps, ban):         def is_en(yx):             if yx[0] < 0 or yx[0] > len(maps)-1: return             if yx[1] < 0 or yx[1] > len(maps[0])-1: return             if yx in ban: return             return True 

В цикле в словарь посещённых с ключом координата записываем начальное значение ноль. В словарь иерархии с таким же ключом записываем иерархическое имя этой координаты. Потом заполняем словарь соседей, вызывая функцию is_en.

for y in range(len(maps)):             for x in range(len(maps[0])):                 self.visit[(y,x)] = 0                 P.hierarchy[(y,x)] = maps[y][x]                                  n = []                 if is_en((y-1,x)):                     n.append((y-1,x))                 if is_en((y+1,x)):                     n.append((y+1,x))                 if is_en((y,x-1)):                     n.append((y,x-1))                 if is_en((y,x+1)):                     n.append((y,x+1))                                      P.neighbor[(y,x)] = n 

Метод show закрашивает клетку с координатами y,x в нужный цвет.

def show(self, y, x, color):         lb = Label(text=" ", background = color)         lb.configure(text=P.hierarchy[(y,x)] )         lb.grid(row=self.y, column=self.x, ipadx=10, ipady=5, padx=1, pady=1) 

Метод move передвигает объект. В переменные y, x записываем координату соседа, которая выбрана случайно из списка соседей. Потом перерисовываем с новыми координатами.

Увеличиваем счётчик. Проверяем достигнута ли цель. Если цель достигнута, то в переменную класса J.disable записываем верно. Вызываем метод top_show().

def move(self):         v = []         for i in P.neighbor[(self.y, self.x)]:             v.append(i)             y,x = random.choice(v)                  self.show(self.y, self.x, 'white')             self.y = y         self.x = x         self.show(y, x, self.color)                  self.count +=1                  if P.target == P.hierarchy[(self.y, self.x)]:             J.disable = True             self.top_show() 

Метод update вызывает move и обновляет через каждые пол секунды.

def update(self):         self.move()            self.root.after(500, self.update) 

Метод top_show выводит результаты.

def top_show(self):         top = Toplevel()         lbt = Label(top, text=self.color + " = " + str(self.count))         lbt.pack()         top.mainloop() 

2.2. Класс М – случайный поиск с памятью

Конструктор вызывает конструктор класса родителя и увеличивает значение той клетки поля, куда идём. Метод move передвигает объект. Метод choice возвращает координату, которую выбрал алгоритм случайного поиска с памятью.

В методе move вызываем метод choice. В остальном его работа аналогична методу родителя.

def move(self):         yx = self.choice((self.y, self.x)) 

Метод choice перебирает координаты соседей и добавляет в список v кортежи. Первым элементом кортежа будет координата соседа, вторым — сколько раз она была посещена. Потом сортируем от маленького до большого. Перебираем все кортежи и в списке v оставляем только те, которые встречались столько раз, сколько записано в первом кортеже. Случайно выбираем любой из них.

def choice(self, yx):         v = []         for i in P.neighbor[yx]:             v.append((i, self.visit[i]))                       v.sort(key = lambda x: x[1], reverse = False)         v = [i for i in v if v[0][1] == i[1]]         v = random.choice(v)         self.visit[v[0]] += 1         return v[0] 

2.3. Класс N – случайный поиск с памятью и иерархией.

Метод choice отбирает те кортежи, которые больше всего совпадают с иерархическим именем цели. Если это совпадение одинаково, то отбираются те координаты, которые меньше всего были посещены.

Конструктор вызывает конструктор родителя и инициализирует атрибут coincidence количество посимвольных совпадений иерархического имени с целью.

def __init__(self, root, color, node, target, maps, ban):         super().__init__(root, color, node, target, maps, ban)         self.coincidence = 0 

Метод choice в цикле записывает иерархическое имя текущей координаты соседа. Переменная r хранит количество совпадений этой переменной c целью. Если количество совпадений r больше, то coincidence перезаписывается. В этом случае в список d записывается кортеж (координаты, количество посещений) из списка v. Если же r равно coincidence, то в d добавляем кортеж из v.

def choice(self, yx):         v = []         for i in P.neighbor[yx]:             v.append((i, self.visit[i]))                       d = []         for l in v:             c = P.hierarchy[l[0]]                          r = 0             for i in range(len(P.target)):                 if c[i] == P.target[i]:                     r +=1                 else: break                               if r > self.coincidence:                 self.coincidence = r                 d = [l]             if r == self.coincidence:                 d.append(l)                  if d: v = d                      v.sort(key = lambda x: x[1], reverse = False)         v = [i for i in v if v[0][1] == i[1]]         v = random.choice(v)         self.visit[v[0]] += 1         return v[0]    

2.4. Класс К – поиск маршрута и поиск самого короткого маршрута.

Конструктор в переменную end записывает координаты цели. Если параметр short равен False, то вызываем поиск маршрута. Иначе находим самый короткий маршрут.

Метод find_path в маршрут добавляет текущую координату. Если цель достигнута, тогда возвращает маршрут. Перебираем всех соседей. Если соседа нет в path, тогда рекурсивно вызываем себя. И то, что метод возвращает, записываем в newpath. Первым найденным маршрутом будет newpath. Отличие метода find_short_path в том, что, если длина newpath больше или равна длине shortest, тогда shortest перезаписывается.

def find_path(self, node, end, path=[]):         path = path + [node]         if node == end:             return path                      for v in P.neighbor[node]:             if v not in path:                 newpath = self.find_path(v, end, path)                 if newpath:                      return newpath 

2.5. Класс J – подвижная цель.

Объект класса J передвигается как класс P. Если переменная класса J.disable равна верно, то объект останавливается.

2.6. Функция main.

Создаём главное окно. В переменную target записываем иерархическое имя цели. В список ban записаны координаты запретных клеток. В списке maps – сама карта. В цикле рисуем карту. Создаём объекты класса. Потом вызываем функцию update, которая обновляет объекты.

3. Выводы

Были проведены пять экспериментов как для подвижной цели, так и для неподвижной.

Из таблицы 1 видно, что самый лучший алгоритм – самый короткий маршрут. Второй по эффективности — это случайный поиск с памятью и иерархией. Худший – случайный поиск.

Таблица 1. Неподвижная цель

image

В случае подвижной цели результаты иные. Худшими стали те алгоритмы, которые изначально рассчитывали маршрут до цели. В таблице 2 прочерк означает, что цель не достигнута. Brown оказался хуже, чем Violet. Хуже, поскольку у Violet траектория длиньше (шанс встретить подвижную цель больше). Самый лучший алгоритм при поиске подвижной цели — случайный поиск с памятью и иерархией. На втором месте — случайный поиск с памятью.

Таблица 2. Подвижная цель

image

Если неизвестно подвижна ли цель или нет, то лучше использовать алгоритм случайный поиск с памятью и иерархией либо просто случайный поиск с памятью.

4. Заключение

Случайность важна, если поиск цели происходит в условиях неизвестности. Поиск сокращается, если данные представлены иерархично. Память, разумеется, тоже ценна.


ссылка на оригинал статьи https://habr.com/ru/post/479414/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *