N (Насти) алгоритм

от автора

Памяти Насти. Памяти дочери.

Что знаем об алгоритмах поиска? Есть граф. Чаще ориентированный. И некое целевое состояние. Фиксированное. А если нет?

Как, например, найти ребенка, который потерялся в лесу? Ведь не только вы его будете искать, но и он вас.

Передвигаться случайно? Да. Но еще лучше выбирать те направления, где меньше всего были. Есть дополнительные признаки, например следы? Отлично. В первую очередь ориентируемся на них. Потерялись следы? Вновь возвращаемся к поиску с учетом только памяти.

while True:     if есть совпадение по признакам:                      #N         выбрать направление с более точным совпадением     else:         if количество посещений по направлениям разное:   #M             выбрать менее посещаемое направление         else:                                             #P             выбрать случайное направление

Что значит выбор с более точным совпадением признаков? Пусть есть соседние состояния {12*, 11*, 14*, 212, 213, 221, 225}. Если целью является состояние 218, то наибольшее совпадение будет либо с 212, либо с 213. Если допустить, что 213 посещалось меньшее число раз, то выбор будет сделан в пользу этого состояния.

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

Сама идея такого поиска — слегка переработанный вариант (код + комментарий кода Насти). Может ошибаюсь, но такой реализации не встречал, как и не встречал самого алгоритма.

Попробуем оценить эффективность кода. Было проведено 10 испытаний как для объекта J(P), так и для объекта J(M). Здесь желтый объект J — подвижное целевое состояние. P — использует только случайный поиск. M — метод поиска, который использует информацию о количестве посещений соседних состояний. N — ищет в первую очередь по признакам, если таковые имеются.

J(P)

J(M)

P

M

N

P

M

N

1

>50

31

7

20

17

9

2

>50

43

15

18

38

38

3

32

12

25

>50

36

18

4

40

34

8

>50

50

5

5

>50

13

12

13

6

6

6

39

8

23

24

24

15

7

>50

14

7

>50

37

11

8

>50

29

13

29

8

8

9

10

7

13

17

26

9

10

27

21

9

15

9

8

Среднее до цели

39.8

21.2

13.2

28.6

25.1

12.7

Разумеется, именно N в выигрыше в большинстве случаев. Существенна разница для поиска методом P. Объясняется тем, что цель J(M) в сравнении с целью J(P) охватывает больше площади, поэтому и встретиться с P имеет большую вероятность.

from tkinter import * import random  TIME = 10  class Z():     def __init__(self, root, maps, ban):         self.root = root         self.maps = maps         self.ban = ban         Z.time = {}         Z.hierarchy = {}         Z.neighbor = {}                    self.init_3_dict(self.maps, self.ban)              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                      for y in range(len(self.maps)):             for x in range(len(self.maps[0])):                 Z.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))                                      Z.neighbor[(y,x)] = n                                  self.time[(y,x)] = 0                 if (y, x) in self.ban:                     lb = Label(text=" ", background = 'black')                     lb.configure(text=self.maps[y][x])                 else:                     lb = Label(text=" ", background = 'white')                     lb.configure(text=self.maps[y][x])                 lb.grid(row=y, column=x, ipadx=10, ipady=5, padx=1, pady=1)                  self.update()                                  def update(self):         for y in range(len(self.maps)):             for x in range(len(self.maps[0])):                 if Z.time[(y,x)]:                     Z.time[(y,x)] -=1                 else:                    Z.hierarchy[(y,x)] = maps[y][x]                                         if not (y, x) in self.ban:                        lb = Label(text=" ", background = 'white')                        lb.configure(text=Z.hierarchy[(y,x)])                        lb.grid(row=y, column=x, ipadx=10, ipady=5, padx=1, pady=1)                          self.root.after(500, self.update)                   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]         self.target = target         self.count = 0                  self.visit = {}         P.neighbor = {}          self.visit[(self.y, self.x)] = 1         P.neighbor[(self.y, self.x)] = Z.neighbor[(self.y, self.x)]                      def update(self):         self.move()            self.root.after(500, self.update)      def show(self, y, x, color):         lb = Label(text=" ", background = color)         lb.configure(text=Z.hierarchy[(y,x)] )         lb.grid(row=self.y, column=self.x, ipadx=10, ipady=5, padx=1, pady=1)              def choice(self, yx):         v = []                  for i in P.neighbor[yx]:              if not yx in self.visit:                 self.visit[yx] = 0             v.append(i)             return random.choice(v)              def move(self):         for i in Z.neighbor[(self.y, self.x)]:             if (self.y, self.x) not in P.neighbor:                 P.neighbor[(self.y, self.x)] = Z.neighbor[(self.y, self.x)]                      y,x = self.choice((self.y, self.x))                  self.show(self.y, self.x, 'white')             self.y = y         self.x = x         self.show(y, x, self.color)                  self.count +=1                           if self.target == Z.hierarchy[(self.y, self.x)]:             #J.disable = True             self.top_show()                        def update(self):         self.move()            self.root.after(500, self.update)              def top_show(self):         top = Toplevel()         lbt = Label(top, text=self.color + " = " + str(self.count))         lbt.pack()         top.mainloop()                   class M(P):     def __init__(self, root, color, node, target, maps, ban):         super().__init__(root, color, node, target, maps, ban)         self.visit[node] += 1              def move(self):         yx = self.choice((self.y, self.x))                  self.show(self.y, self.x, 'white')             self.y = yx[0]         self.x = yx[1]         self.show(yx[0], yx[1], self.color)                  self.count +=1                  if self.target == Z.hierarchy[(self.y, self.x)]:             #J.disable = True             self.top_show()              def choice(self, yx):         v = []                  for i in Z.neighbor[yx]:             if (yx) not in P.neighbor:                 P.neighbor[yx] = Z.neighbor[yx]                          for i in P.neighbor[yx]:              if not i in self.visit:                 self.visit[i] = 0             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]                   class N(M):     def __init__(self, root, color, node, target, maps, ban):         super().__init__(root, color, node, target, maps, ban)         self.coincidence = 0              def choice(self, yx):         v = []                  for i in Z.neighbor[yx]:             if (yx) not in P.neighbor:                 P.neighbor[yx] = Z.neighbor[yx]                          for i in P.neighbor[yx]:              if not i in self.visit:                 self.visit[i] = 0             v.append((i, self.visit[i]))                       d = []         for l in v:             c = Z.hierarchy[l[0]]                          r = 0             for i in range(len(self.target)):                 if c[i] == self.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]                      class J(P): #J(P) or J(M)     def __init__(self, root, color, node, target, maps, ban):         super().__init__(root, color, node, target, maps, ban)         J.disable = False              def move(self):         if not J.disable:             v = []                          for i in Z.neighbor[(self.y, self.x)]:                 if (self.y, self.x) not in P.neighbor:                     P.neighbor[(self.y, self.x)] = Z.neighbor[(self.y, self.x)]                              y,x = self.choice((self.y, self.x))                          Z.hierarchy[(self.y, self.x)] = self.target[:-1] + \               str(int(self.target[-1]) - 1) #'4288'             Z.time[(y, x)] = TIME                              self.show(self.y, self.x, 'white')                self.y = y             self.x = x             Z.hierarchy[(self.y, self.x)] = self.target             self.show(y, x, self.color)               #--------- main ---------#     if __name__ == "__main__":          root = Tk()          target = ('4289')     ban=[(1,1), (1,2), (2,3), (5,5)]           '''     maps = [ ["011", "012", "013", "211", "212", "201"],              ["014", "015", "021", "213", "214", "202"],              ["022", "023", "024", "215", "216", "203"],              ["025", "026", "027", "213", "204", "205"],              ["101", "102", "103", "121", "122", "123"],              ["104", "105", "106", "124", "125", "126"]     ]     '''          maps = [ ["0111", "0121", "0132", "2112", "2121", "2013", "2011"],              ["0141", "0152", "0211", "2132", "2143", "2021", "2013"],              ["0221", "0231", "0242", "2151", "2162", "2031", "2033"],              ["0252", "0262", "0271", "2131", "2041", "2052", "2032"],              ["1011", "1022", "1032", "1212", "1231", "1231", "1233"],              ["1042", "1053", "1062", "1241", "1233", "1262", "1261"],              ["1041", "1052", "1064", "1245", "1255", "1266", "1264"],     ]               def update():         z1.update()         p1.update()         m1.update()         n1.update()         j1.update()              z1 = Z(root, maps, ban)     p1 = P(root, 'red',  (1,0), target, z1.maps, z1.ban)     m1 = M(root, 'blue',  (1,0), target, z1.maps, z1.ban)     n1 = N(root, 'green',  (1,0), target, z1.maps, z1.ban)     j1 = J(root, 'yellow',  (4,4), target, z1.maps, z1.ban)          root.after(500, update)       root.mainloop()


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


Комментарии

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

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