Advertisement
Guest User

Untitled

a guest
Jan 27th, 2020
141
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.27 KB | None | 0 0
  1. import math
  2. import random
  3.  
  4. import bisect
  5.  
  6.  
  7. """
  8. Дефинирање на класа за структурата на проблемот кој ќе го решаваме со пребарување.
  9. Класата Problem е апстрактна класа од која правиме наследување за дефинирање на основните
  10. карактеристики на секој проблем што сакаме да го решиме
  11. """
  12.  
  13.  
  14. class Problem:
  15. def __init__(self, initial, goal=None):
  16. self.initial = initial
  17. self.goal = goal
  18.  
  19. def successor(self, state):
  20. """За дадена состојба, врати речник од парови {акција : состојба}
  21. достапни од оваа состојба. Ако има многу следбеници, употребете
  22. итератор кој би ги генерирал следбениците еден по еден, наместо да
  23. ги генерирате сите одеднаш.
  24. :param state: дадена состојба
  25. :return: речник од парови {акција : состојба} достапни од оваа
  26. состојба
  27. :rtype: dict
  28. """
  29. raise NotImplementedError
  30.  
  31. def actions(self, state):
  32. """За дадена состојба state, врати листа од сите акции што може да
  33. се применат над таа состојба
  34. :param state: дадена состојба
  35. :return: листа на акции
  36. :rtype: list
  37. """
  38. return self.successor(state).keys()
  39.  
  40. def result(self, state, action):
  41. """За дадена состојба state и акција action, врати ја состојбата
  42. што се добива со примена на акцијата над состојбата
  43. :param state: дадена состојба
  44. :param action: дадена акција
  45. :return: резултантна состојба
  46. """
  47. possible = self.successor(state)
  48. return possible[action]
  49.  
  50.  
  51. def goal_test(self, state):
  52. """Врати True ако state е целна состојба. Даденава имплементација
  53. на методот директно ја споредува state со self.goal, како што е
  54. специфицирана во конструкторот. Имплементирајте го овој метод ако
  55. проверката со една целна состојба self.goal не е доволна.
  56. :param state: дадена состојба
  57. :return: дали дадената состојба е целна состојба
  58. :rtype: bool
  59. """
  60. return state == self.goal
  61.  
  62. def path_cost(self, c, state1, action, state2):
  63. """Врати ја цената на решавачкиот пат кој пристигнува во состојбата
  64. state2 од состојбата state1 преку акцијата action, претпоставувајќи
  65. дека цената на патот до состојбата state1 е c. Ако проблемот е таков
  66. што патот не е важен, оваа функција ќе ја разгледува само состојбата
  67. state2. Ако патот е важен, ќе ја разгледува цената c и можеби и
  68. state1 и action. Даденава имплементација му доделува цена 1 на секој
  69. чекор од патот.
  70. :param c: цена на патот до состојбата state1
  71. :param state1: дадена моментална состојба
  72. :param action: акција која треба да се изврши
  73. :param state2: состојба во која треба да се стигне
  74. :return: цена на патот по извршување на акцијата
  75. :rtype: float
  76. """
  77. return c + 1
  78.  
  79. def value(self):
  80. """За проблеми на оптимизација, секоја состојба си има вредност.
  81. Hill-climbing и сличните алгоритми се обидуваат да ја максимизираат
  82. оваа вредност.
  83. :return: вредност на состојба
  84. :rtype: float
  85. """
  86. raise NotImplementedError
  87.  
  88.  
  89. """
  90. Дефинирање на класата за структурата на јазел од пребарување.
  91. Класата Node не се наследува
  92. """
  93.  
  94.  
  95. class Node:
  96. def __init__(self, state, parent=None, action=None, path_cost=0):
  97. """Креирај јазол од пребарувачкото дрво, добиен од parent со примена
  98. на акцијата action
  99. :param state: моментална состојба (current state)
  100. :param parent: родителска состојба (parent state)
  101. :param action: акција (action)
  102. :param path_cost: цена на патот (path cost)
  103. """
  104. self.state = state
  105. self.parent = parent
  106. self.action = action
  107. self.path_cost = path_cost
  108. self.depth = 0 # search depth
  109. if parent:
  110. self.depth = parent.depth + 1
  111.  
  112. def __repr__(self):
  113. return "<Node %s>" % (self.state,)
  114.  
  115. def __lt__(self, node):
  116. return self.state < node.state
  117.  
  118. def expand(self, problem):
  119. """Излистај ги јазлите достапни во еден чекор од овој јазол.
  120. :param problem: даден проблем
  121. :return: листа на достапни јазли во еден чекор
  122. :rtype: list(Node)
  123. """
  124. return [self.child_node(problem, action)
  125. for action in problem.actions(self.state)]
  126.  
  127. def child_node(self, problem, action):
  128. """Дете јазел
  129. :param problem: даден проблем
  130. :param action: дадена акција
  131. :return: достапен јазел според дадената акција
  132. :rtype: Node
  133. """
  134. next_state = problem.result(self.state, action)
  135. return Node(next_state, self, action,
  136. problem.path_cost(self.path_cost, self.state,
  137. action, next_state))
  138.  
  139. def solution(self):
  140. """Врати ја секвенцата од акции за да се стигне од коренот до овој јазол.
  141. :return: секвенцата од акции
  142. :rtype: list
  143. """
  144. return [node.action for node in self.path()[1:]]
  145.  
  146. def solve(self):
  147. """Врати ја секвенцата од состојби за да се стигне од коренот до овој јазол.
  148. :return: листа од состојби
  149. :rtype: list
  150. """
  151. return [node.state for node in self.path()[0:]]
  152.  
  153. def path(self):
  154. """Врати ја листата од јазли што го формираат патот од коренот до овој јазол.
  155. :return: листа од јазли од патот
  156. :rtype: list(Node)
  157. """
  158. x, result = self, []
  159. while x:
  160. result.append(x)
  161. x = x.parent
  162. result.reverse()
  163. return result
  164.  
  165. """Сакаме редицата од јазли кај breadth_first_search или
  166. astar_search да не содржи состојби - дупликати, па јазлите што
  167. содржат иста состојба ги третираме како исти. [Проблем: ова може
  168. да не биде пожелно во други ситуации.]"""
  169.  
  170. def __eq__(self, other):
  171. return isinstance(other, Node) and self.state == other.state
  172.  
  173. def __hash__(self):
  174. return hash(self.state)
  175.  
  176.  
  177. """
  178. Дефинирање на помошни структури за чување на листата на генерирани, но непроверени јазли
  179. """
  180.  
  181.  
  182. class Queue:
  183. """Queue е апстрактна класа / интерфејс. Постојат 3 типа:
  184. Stack(): Last In First Out Queue (стек).
  185. FIFOQueue(): First In First Out Queue (редица).
  186. PriorityQueue(order, f): Queue во сортиран редослед (подразбирливо,од најмалиот кон
  187. најголемиот јазол).
  188. """
  189.  
  190. def __init__(self):
  191. raise NotImplementedError
  192.  
  193. def append(self, item):
  194. """Додади го елементот item во редицата
  195. :param item: даден елемент
  196. :return: None
  197. """
  198. raise NotImplementedError
  199.  
  200. def extend(self, items):
  201. """Додади ги елементите items во редицата
  202. :param items: дадени елементи
  203. :return: None
  204. """
  205. raise NotImplementedError
  206.  
  207. def pop(self):
  208. """Врати го првиот елемент од редицата
  209. :return: прв елемент
  210. """
  211. raise NotImplementedError
  212.  
  213. def __len__(self):
  214. """Врати го бројот на елементи во редицата
  215. :return: број на елементи во редицата
  216. :rtype: int
  217. """
  218. raise NotImplementedError
  219.  
  220. def __contains__(self, item):
  221. """Проверка дали редицата го содржи елементот item
  222. :param item: даден елемент
  223. :return: дали queue го содржи item
  224. :rtype: bool
  225. """
  226. raise NotImplementedError
  227.  
  228.  
  229. class Stack(Queue):
  230. """Last-In-First-Out Queue."""
  231.  
  232. def __init__(self):
  233. self.data = []
  234.  
  235. def append(self, item):
  236. self.data.append(item)
  237.  
  238. def extend(self, items):
  239. self.data.extend(items)
  240.  
  241. def pop(self):
  242. return self.data.pop()
  243.  
  244. def __len__(self):
  245. return len(self.data)
  246.  
  247. def __contains__(self, item):
  248. return item in self.data
  249.  
  250.  
  251. class FIFOQueue(Queue):
  252. """First-In-First-Out Queue."""
  253.  
  254. def __init__(self):
  255. self.data = []
  256.  
  257. def append(self, item):
  258. self.data.append(item)
  259.  
  260. def extend(self, items):
  261. self.data.extend(items)
  262.  
  263. def pop(self):
  264. return self.data.pop(0)
  265.  
  266. def __len__(self):
  267. return len(self.data)
  268.  
  269. def __contains__(self, item):
  270. return item in self.data
  271.  
  272.  
  273. class PriorityQueue(Queue):
  274. """Редица во која прво се враќа минималниот (или максималниот) елемент
  275. (како што е определено со f и order). Оваа структура се користи кај
  276. информирано пребарување"""
  277. """"""
  278.  
  279. def __init__(self, order=min, f=lambda x: x):
  280. """
  281. :param order: функција за подредување, ако order е min, се враќа елементот
  282. со минимална f(x); ако order е max, тогаш се враќа елементот
  283. со максимална f(x).
  284. :param f: функција f(x)
  285. """
  286. assert order in [min, max]
  287. self.data = []
  288. self.order = order
  289. self.f = f
  290.  
  291. def append(self, item):
  292. bisect.insort_right(self.data, (self.f(item), item))
  293.  
  294. def extend(self, items):
  295. for item in items:
  296. bisect.insort_right(self.data, (self.f(item), item))
  297.  
  298. def pop(self):
  299. if self.order == min:
  300. return self.data.pop(0)[1]
  301. return self.data.pop()[1]
  302.  
  303. def __len__(self):
  304. return len(self.data)
  305.  
  306. def __contains__(self, item):
  307. return any(item == pair[1] for pair in self.data)
  308.  
  309. def __getitem__(self, key):
  310. for _, item in self.data:
  311. if item == key:
  312. return item
  313.  
  314. def __delitem__(self, key):
  315. for i, (value, item) in enumerate(self.data):
  316. if item == key:
  317. self.data.pop(i)
  318.  
  319.  
  320. def distance(a, b):
  321. """The distance between two (x, y) points."""
  322. return math.hypot((a[0] - b[0]), (a[1] - b[1]))
  323.  
  324.  
  325. class Graph:
  326. """A graph connects nodes (verticies) by edges (links). Each edge can also
  327. have a length associated with it. The constructor call is something like:
  328. g = Graph({'A': {'B': 1, 'C': 2})
  329. this makes a graph with 3 nodes, A, B, and C, with an edge of length 1 from
  330. A to B, and an edge of length 2 from A to C. You can also do:
  331. g = Graph({'A': {'B': 1, 'C': 2}, directed=False)
  332. This makes an undirected graph, so inverse links are also added. The graph
  333. stays undirected; if you add more links with g.connect('B', 'C', 3), then
  334. inverse link is also added. You can use g.nodes() to get a list of nodes,
  335. g.get('A') to get a dict of links out of A, and g.get('A', 'B') to get the
  336. length of the link from A to B. 'Lengths' can actually be any object at
  337. all, and nodes can be any hashable object."""
  338.  
  339. def __init__(self, dictionary=None, directed=True):
  340. self.dict = dictionary or {}
  341. self.directed = directed
  342. if not directed:
  343. self.make_undirected()
  344.  
  345. def make_undirected(self):
  346. """Make a digraph into an undirected graph by adding symmetric edges."""
  347. for a in list(self.dict.keys()):
  348. for (b, dist) in self.dict[a].items():
  349. self.connect1(b, a, dist)
  350.  
  351. def connect(self, node_a, node_b, distance_val=1):
  352. """Add a link from node_a and node_b of given distance_val, and also add the inverse
  353. link if the graph is undirected."""
  354. self.connect1(node_a, node_b, distance_val)
  355. if not self.directed:
  356. self.connect1(node_b, node_a, distance_val)
  357.  
  358. def connect1(self, node_a, node_b, distance_val):
  359. """Add a link from node_a to node_b of given distance_val, in one direction only."""
  360. self.dict.setdefault(node_a, {})[node_b] = distance_val
  361.  
  362. def get(self, a, b=None):
  363. """Return a link distance or a dict of {node: distance} entries.
  364. .get(a,b) returns the distance or None;
  365. .get(a) returns a dict of {node: distance} entries, possibly {}."""
  366. links = self.dict.setdefault(a, {})
  367. if b is None:
  368. return links
  369. else:
  370. return links.get(b)
  371.  
  372. def nodes(self):
  373. """Return a list of nodes in the graph."""
  374. return list(self.dict.keys())
  375.  
  376.  
  377. def UndirectedGraph(dictionary=None):
  378. """Build a Graph where every edge (including future ones) goes both ways."""
  379. return Graph(dictionary=dictionary, directed=False)
  380.  
  381.  
  382. def RandomGraph(nodes=list(range(10)), min_links=2, width=400, height=300,
  383. curvature=lambda: random.uniform(1.1, 1.5)):
  384. """Construct a random graph, with the specified nodes, and random links.
  385. The nodes are laid out randomly on a (width x height) rectangle.
  386. Then each node is connected to the min_links nearest neighbors.
  387. Because inverse links are added, some nodes will have more connections.
  388. The distance between nodes is the hypotenuse times curvature(),
  389. where curvature() defaults to a random number between 1.1 and 1.5."""
  390. g = UndirectedGraph()
  391. g.locations = {}
  392. # Build the cities
  393. for node in nodes:
  394. g.locations[node] = (random.randrange(width), random.randrange(height))
  395. # Build roads from each city to at least min_links nearest neighbors.
  396. for i in range(min_links):
  397. for node in nodes:
  398. if len(g.get(node)) < min_links:
  399. here = g.locations[node]
  400.  
  401. def distance_to_node(n):
  402. if n is node or g.get(node, n):
  403. return math.inf
  404. return distance(g.locations[n], here)
  405.  
  406. neighbor = nodes.index(min(nodes, key=distance_to_node))
  407. d = distance(g.locations[neighbor], here) * curvature()
  408. g.connect(node, neighbor, int(d))
  409. return g
  410.  
  411.  
  412. class GraphProblem(Problem):
  413. """The problem of searching a graph from one node to another."""
  414.  
  415. def __init__(self, initial, goal, graph):
  416. super().__init__(initial, goal)
  417. self.graph = graph
  418.  
  419. def actions(self, state):
  420. """The actions at a graph node are just its neighbors."""
  421. return list(self.graph.get(state).keys())
  422.  
  423. def result(self, state, action):
  424. """The result of going to a neighbor is just that neighbor."""
  425. return action
  426.  
  427. def path_cost(self, c, state1, action, state2):
  428. return c + (self.graph.get(state1, state2) or math.inf)
  429.  
  430. def h(self, node):
  431. """h function is straight-line distance from a node's state to goal."""
  432. return 1
  433.  
  434. import sys
  435.  
  436. """
  437. Неинформирано пребарување во рамки на дрво.
  438. Во рамки на дрвото не разрешуваме јамки.
  439. """
  440.  
  441.  
  442. def tree_search(problem, fringe):
  443. """ Пребарувај низ следбениците на даден проблем за да најдеш цел.
  444.  
  445. :param problem: даден проблем
  446. :param fringe: празна редица (queue)
  447. :return: Node
  448. """
  449. fringe.append(Node(problem.initial))
  450. while fringe:
  451. node = fringe.pop()
  452. print(node.state)
  453. if problem.goal_test(node.state):
  454. return node
  455. fringe.extend(node.expand(problem))
  456. return None
  457.  
  458.  
  459. def breadth_first_tree_search(problem):
  460. """Експандирај го прво најплиткиот јазол во пребарувачкото дрво.
  461.  
  462. :param problem: даден проблем
  463. :return: Node
  464. """
  465. return tree_search(problem, FIFOQueue())
  466.  
  467.  
  468. def depth_first_tree_search(problem):
  469. """Експандирај го прво најдлабокиот јазол во пребарувачкото дрво.
  470.  
  471. :param problem:даден проблем
  472. :return: Node
  473. """
  474. return tree_search(problem, Stack())
  475.  
  476.  
  477. """
  478. Неинформирано пребарување во рамки на граф
  479. Основната разлика е во тоа што овде не дозволуваме јамки,
  480. т.е. повторување на состојби
  481. """
  482.  
  483.  
  484. def graph_search(problem, fringe):
  485. """Пребарувај низ следбениците на даден проблем за да најдеш цел.
  486. Ако до дадена состојба стигнат два пата, употреби го најдобриот пат.
  487.  
  488. :param problem: даден проблем
  489. :param fringe: празна редица (queue)
  490. :return: Node
  491. """
  492. closed = set()
  493. fringe.append(Node(problem.initial))
  494. while fringe:
  495. node = fringe.pop()
  496. if problem.goal_test(node.state):
  497. return node
  498. if node.state not in closed:
  499. closed.add(node.state)
  500. fringe.extend(node.expand(problem))
  501. return None
  502.  
  503.  
  504. def breadth_first_graph_search(problem):
  505. """Експандирај го прво најплиткиот јазол во пребарувачкиот граф.
  506.  
  507. :param problem: даден проблем
  508. :return: Node
  509. """
  510. return graph_search(problem, FIFOQueue())
  511.  
  512.  
  513. def depth_first_graph_search(problem):
  514. """Експандирај го прво најдлабокиот јазол во пребарувачкиот граф.
  515.  
  516. :param problem: даден проблем
  517. :return: Node
  518. """
  519. return graph_search(problem, Stack())
  520.  
  521.  
  522. def depth_limited_search(problem, limit=50):
  523. def recursive_dls(node, problem, limit):
  524. """Помошна функција за depth limited"""
  525. cutoff_occurred = False
  526. if problem.goal_test(node.state):
  527. return node
  528. elif node.depth == limit:
  529. return 'cutoff'
  530. else:
  531. for successor in node.expand(problem):
  532. result = recursive_dls(successor, problem, limit)
  533. if result == 'cutoff':
  534. cutoff_occurred = True
  535. elif result is not None:
  536. return result
  537. if cutoff_occurred:
  538. return 'cutoff'
  539. return None
  540.  
  541. return recursive_dls(Node(problem.initial), problem, limit)
  542.  
  543.  
  544. def iterative_deepening_search(problem):
  545. for depth in range(sys.maxsize):
  546. result = depth_limited_search(problem, depth)
  547. if result is not 'cutoff':
  548. return result
  549.  
  550.  
  551. def uniform_cost_search(problem):
  552. """Експандирај го прво јазолот со најниска цена во пребарувачкиот граф."""
  553. return graph_search(problem, PriorityQueue(min, lambda a: a.path_cost))
  554.  
  555.  
  556. Pocetok = input()
  557. Kraj = input()
  558. Stanica = input()
  559.  
  560. graph = UndirectedGraph({
  561. "A": {"B": 7, "C": 9, "F": 14},
  562. "B": {"C": 10, "D": 15},
  563. "C": {"D": 11, "F": 2},
  564. "D": {"E": 6},
  565. "E": {"F": 9}
  566. })
  567.  
  568.  
  569. graph_problem_startend = GraphProblem(Pocetok, Kraj, graph)
  570. graph_problem_stanica1 = GraphProblem(Pocetok, Stanica, graph)
  571. graph_problem_stanica2 = GraphProblem(Stanica, Kraj, graph)
  572.  
  573. distance1 = uniform_cost_search(graph_problem_startend).path_cost
  574. distance2 = uniform_cost_search(graph_problem_stanica1).path_cost + uniform_cost_search(graph_problem_stanica2).path_cost - 9
  575.  
  576. #print("Start - Kraj:" + str(astar_search(graph_problem_startend).solve()) + " CENA: " + str(astar_search(graph_problem_startend).path_cost))
  577. #print("Start - Stanica:" + str(astar_search(graph_problem_stanica1).solve()) + " CENA: " + str(astar_search(graph_problem_stanica1).path_cost))
  578. #print("Stanica - Kraj:" + str(astar_search(graph_problem_stanica2).solve()) + " CENA: " + str(astar_search(graph_problem_stanica2).path_cost))
  579.  
  580. print(min(distance1, distance2))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement