This commit is contained in:
Dmitry Belyaev 2024-04-25 10:43:01 +03:00
parent e96d20343b
commit c22ef9e724

View File

@ -12,7 +12,8 @@ Link - для описания связи между двумя произвол
LinkedGraph - для представления связного графа в целом (карта целиком). LinkedGraph - для представления связного графа в целом (карта целиком).
Объекты класса Vertex должны создаваться командой: Объекты класса Vertex должны создаваться командой:
v = Vertex() >>> v = Vertex()
и содержать локальный атрибут: и содержать локальный атрибут:
_links - список связей с другими вершинами графа (список объектов класса Link). _links - список связей с другими вершинами графа (список объектов класса Link).
@ -20,7 +21,9 @@ _links - список связей с другими вершинами граф
links - для получения ссылки на список _links. links - для получения ссылки на список _links.
Объекты следующего класса Link должны создаваться командой: Объекты следующего класса Link должны создаваться командой:
link = Link(v1, v2) >>> v1 = Vertex(); v2 = Vertex()
>>> link = Link(v1, v2)
где v1, v2 - объекты класса Vertex (вершины графа). Внутри каждого объекта класса Link должны формироваться следующие локальные атрибуты: где v1, v2 - объекты класса Vertex (вершины графа). Внутри каждого объекта класса Link должны формироваться следующие локальные атрибуты:
_v1, _v2 - ссылки на объекты класса Vertex, которые соединяются данной связью; _v1, _v2 - ссылки на объекты класса Vertex, которые соединяются данной связью;
@ -32,7 +35,7 @@ v2 - для получения ссылки на вершину v2;
dist - для изменения и считывания значения атрибута _dist. dist - для изменения и считывания значения атрибута _dist.
Наконец, объекты третьего класса LinkedGraph должны создаваться командой: Наконец, объекты третьего класса LinkedGraph должны создаваться командой:
map_graph = LinkedGraph() >>> map_graph = LinkedGraph()
В каждом объекте класса LinkedGraph должны формироваться локальные атрибуты: В каждом объекте класса LinkedGraph должны формироваться локальные атрибуты:
_links - список из всех связей графа (из объектов класса Link); _links - список из всех связей графа (из объектов класса Link);
@ -58,30 +61,29 @@ _links = [Link(v1, v2)]
Пример использования классов, применительно к схеме метро (эти строчки в программе писать не нужно): Пример использования классов, применительно к схеме метро (эти строчки в программе писать не нужно):
map_graph = LinkedGraph() >>> Vertex._num = 0
>>> map_graph = LinkedGraph()
v1 = Vertex() >>> v1 = Vertex()
v2 = Vertex() >>> v2 = Vertex()
v3 = Vertex() >>> v3 = Vertex()
v4 = Vertex() >>> v4 = Vertex()
v5 = Vertex() >>> v5 = Vertex()
v6 = Vertex() >>> v6 = Vertex()
v7 = Vertex() >>> v7 = Vertex()
>>> map_graph.add_link(Link(v1, v2))
map_graph.add_link(Link(v1, v2)) >>> map_graph.add_link(Link(v2, v3))
map_graph.add_link(Link(v2, v3)) >>> map_graph.add_link(Link(v1, v3))
map_graph.add_link(Link(v1, v3)) >>> map_graph.add_link(Link(v4, v5))
>>> map_graph.add_link(Link(v6, v7))
map_graph.add_link(Link(v4, v5)) >>> map_graph.add_link(Link(v2, v7))
map_graph.add_link(Link(v6, v7)) >>> map_graph.add_link(Link(v3, v4))
>>> map_graph.add_link(Link(v5, v6))
map_graph.add_link(Link(v2, v7)) >>> len(map_graph._links)
map_graph.add_link(Link(v3, v4)) 8
map_graph.add_link(Link(v5, v6)) >>> len(map_graph._vertex)
7
print(len(map_graph._links)) # 8 связей >>> map_graph.find_path(v1, v6)
print(len(map_graph._vertex)) # 7 вершин ([Vertex('A'), Vertex('B'), Vertex('G'), Vertex('F')], [Link(Vertex('A'), Vertex('B'), 1), Link(Vertex('B'), Vertex('G'), 1), Link(Vertex('F'), Vertex('G'), 1)])
path = map_graph.find_path(v1, v6)
Однако, в таком виде применять классы для схемы карты метро не очень удобно. Однако, в таком виде применять классы для схемы карты метро не очень удобно.
Например, здесь нет указаний названий станций, а также длина каждого сегмента равна 1, что не соответствует действительности. Например, здесь нет указаний названий станций, а также длина каждого сегмента равна 1, что не соответствует действительности.
@ -91,7 +93,8 @@ class Station(Vertex): ... - для описания станций метро;
class LinkMetro(Link): ... - для описания связей между станциями метро. class LinkMetro(Link): ... - для описания связей между станциями метро.
Объекты класса Station должны создаваться командой: Объекты класса Station должны создаваться командой:
st = Station(name) >>> st = Station(name := "Домодедовская")
где name - название станции (строка). В каждом объекте класса Station должен дополнительно формироваться локальный атрибут: где name - название станции (строка). В каждом объекте класса Station должен дополнительно формироваться локальный атрибут:
name - название станции метро. name - название станции метро.
@ -99,37 +102,42 @@ name - название станции метро.
В самом классе Station переопределите магические методы __str__() и __repr__(), чтобы они возвращали название станции метро (локальный атрибут name). В самом классе Station переопределите магические методы __str__() и __repr__(), чтобы они возвращали название станции метро (локальный атрибут name).
Объекты второго класса LinkMetro должны создаваться командой: Объекты второго класса LinkMetro должны создаваться командой:
link = LinkMetro(v1, v2, dist) >>> link = LinkMetro(v1, v2, dist := 2)
где v1, v2 - вершины (станции метро); dist - расстояние между станциями (любое положительное число). где v1, v2 - вершины (станции метро); dist - расстояние между станциями (любое положительное число).
(Также не забывайте в инициализаторе этого дочернего класса вызывать инициализатор базового класса). (Также не забывайте в инициализаторе этого дочернего класса вызывать инициализатор базового класса).
В результате, эти классы должны совместно работать следующим образом (эти строчки в программе писать не нужно): В результате, эти классы должны совместно работать следующим образом (эти строчки в программе писать не нужно):
map_metro = LinkedGraph() >>> map_metro = LinkedGraph()
v1 = Station("Сретенский бульвар") >>> v1 = Station("Сретенский бульвар")
v2 = Station("Тургеневская") >>> v2 = Station("Тургеневская")
v3 = Station("Чистые пруды") >>> v3 = Station("Чистые пруды")
v4 = Station("Лубянка") >>> v4 = Station("Лубянка")
v5 = Station("Кузнецкий мост") >>> v5 = Station("Кузнецкий мост")
v6 = Station("Китай-город 1") >>> v6 = Station("Китай-город 1")
v7 = Station("Китай-город 2") >>> v7 = Station("Китай-город 2")
map_metro.add_link(LinkMetro(v1, v2, 1)) >>> map_metro.add_link(LinkMetro(v1, v2, 1))
map_metro.add_link(LinkMetro(v2, v3, 1)) >>> map_metro.add_link(LinkMetro(v2, v3, 1))
map_metro.add_link(LinkMetro(v1, v3, 1)) >>> map_metro.add_link(LinkMetro(v1, v3, 1))
map_metro.add_link(LinkMetro(v4, v5, 1)) >>> map_metro.add_link(LinkMetro(v4, v5, 1))
map_metro.add_link(LinkMetro(v6, v7, 1)) >>> map_metro.add_link(LinkMetro(v6, v7, 1))
map_metro.add_link(LinkMetro(v2, v7, 5)) >>> map_metro.add_link(LinkMetro(v2, v7, 5))
map_metro.add_link(LinkMetro(v3, v4, 3)) >>> map_metro.add_link(LinkMetro(v3, v4, 3))
map_metro.add_link(LinkMetro(v5, v6, 3)) >>> map_metro.add_link(LinkMetro(v5, v6, 3))
print(len(map_metro._links)) >>> print(len(map_metro._links))
print(len(map_metro._vertex)) 8
path = map_metro.find_path(v1, v6) # от сретенского бульвара до китай-город 1 >>> print(len(map_metro._vertex))
print(path[0]) # [Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1] 7
print(sum([x.dist for x in path[1]])) # 7 >>> path = map_metro.find_path(v1, v6) # от сретенского бульвара до китай-город 1
>>> print(path[0]) # [Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1]
[Сретенский бульвар, Тургеневская, Китай-город 2, Китай-город 1]
>>> print(sum([x.dist for x in path[1]])) # 7
7
P.S. В программе нужно объявить только классы Vertex, Link, LinkedGraph, Station, LinkMetro. На экран ничего выводить не нужно. P.S. В программе нужно объявить только классы Vertex, Link, LinkedGraph, Station, LinkMetro. На экран ничего выводить не нужно.
""" """
@ -137,7 +145,7 @@ P.S. В программе нужно объявить только классы
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass, field from dataclasses import dataclass, field
from functools import total_ordering from functools import total_ordering
from typing import List, Union from typing import List, Optional, Tuple, Union
def make_properties_prot(*names): def make_properties_prot(*names):
@ -159,12 +167,14 @@ def make_properties_prot(*names):
class AutoNamed: class AutoNamed:
_num = 0 _num: int = 0
name: str name: str
@staticmethod @staticmethod
def column_code(num): def column_code(num: int) -> str:
def gen(n): """Имя как код колонки в Excel по порядковому номеру с 1"""
def gen(n: int):
a = ord("A") a = ord("A")
sz = ord("Z") - a + 1 sz = ord("Z") - a + 1
while n: while n:
@ -182,7 +192,7 @@ class AutoNamed:
@make_properties_prot("links") @make_properties_prot("links")
class Vertex(AutoNamed): class Vertex(AutoNamed):
def __init__(self, name=None): def __init__(self, name: Optional[str] = None):
if name: if name:
self.name = name self.name = name
self._links = [] self._links = []
@ -238,7 +248,7 @@ class LinksPath:
return sum([x.dist for x in self.links]) return sum([x.dist for x in self.links])
return float("inf") return float("inf")
def add_link(self, link): def add_link(self, link: Link):
if link not in self.links: if link not in self.links:
self.links.append(link) self.links.append(link)
return self return self
@ -263,52 +273,51 @@ class LinksPath:
@make_properties_prot("links", "vertex") @make_properties_prot("links", "vertex")
class LinkedGraph: class LinkedGraph:
def __init__(self, vertex=None, links=None): def __init__(self, vertex: Optional[Vertex] = None, links: Optional[Link] = None):
self._vertex = vertex or [] self._vertex = vertex or []
self._links = links or [] self._links = links or []
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self.__class__.__name__}{(self.vertex, self.links)!r}" return f"{self.__class__.__name__}{(self.vertex, self.links)!r}"
def add_vertex(self, v): def add_vertex(self, v: Vertex):
if v not in self._vertex: if v not in self._vertex:
self._vertex.append(v) self._vertex.append(v)
def add_link(self, link): def add_link(self, link: Link):
if link not in self._links: if link not in self._links:
self._links.append(link) self._links.append(link)
for v in link: for v in link:
self.add_vertex(v) self.add_vertex(v)
def dijkstras(self, start, stop=None): def dijkstras(self, start: Vertex, stop: Optional[Vertex] = None):
def walk(remaining, paths, current):
while remaining:
remaining.discard(current)
yield current
if current == stop:
break
if remaining:
current = min(remaining, key=lambda x: paths[x])
paths = defaultdict(LinksPath) paths = defaultdict(LinksPath)
paths[start].is_start = True paths[start].is_start = True
remaining, current = set(self.vertex), start remaining = set(self.vertex)
while remaining: for current in walk(remaining, paths, start):
remaining.discard(current)
for link in current: for link in current:
for v in link: for v in filter(lambda v: v in remaining, link):
if v not in remaining:
continue
new_path = paths[current].copy().add_link(link) new_path = paths[current].copy().add_link(link)
if new_path < paths[v]: paths[v] = min(paths[v], new_path)
paths[v] = new_path
if current == stop:
break
if remaining:
current = min(remaining, key=lambda x: paths[x])
return paths return paths
def find_path(self, start_v, stop_v): def find_path(
self, start_v: Vertex, stop_v: Vertex
) -> Tuple[List[Vertex], List[Link]]:
path = self.dijkstras(start_v, stop_v)[stop_v] path = self.dijkstras(start_v, stop_v)[stop_v]
return path.vertex, path.links return path.vertex, path.links
class Station(Vertex): class Station(Vertex):
def __init__(self, name: str):
super().__init__()
self.name = name
def __repr__(self): def __repr__(self):
return self.name return self.name
@ -317,14 +326,9 @@ class LinkMetro(Link):
... ...
vA = Vertex("A") print("-------------------------------------------")
vB = Vertex("B")
vC = Vertex("C")
vD = Vertex("D")
vE = Vertex("E")
vF = Vertex("F")
vG = Vertex("G")
vA, vB, vC, vD, vE, vF, vG = [Vertex() for _ in range(7)]
map_graph = LinkedGraph() map_graph = LinkedGraph()
map_graph.add_link(Link(vA, vB)) map_graph.add_link(Link(vA, vB))
map_graph.add_link(Link(vB, vC)) map_graph.add_link(Link(vB, vC))
@ -340,6 +344,7 @@ map_graph.add_link(Link(vE, vF))
print(len(map_graph._links)) # 8 связей print(len(map_graph._links)) # 8 связей
print(len(map_graph._vertex)) # 7 вершин print(len(map_graph._vertex)) # 7 вершин
print(map_graph.find_path(vA, vG)) print(map_graph.find_path(vA, vG))
# ([Vertex('A'), Vertex('B'), Vertex('G')], [Link(Vertex('A'), Vertex('B'), 1), Link(Vertex('B'), Vertex('G'), 1)])
print("-------------------------------------------") print("-------------------------------------------")
@ -370,6 +375,7 @@ print(path[0]) # [Сретенский бульвар, Тургеневская
print(sum([x.dist for x in path[1]])) # 7 print(sum([x.dist for x in path[1]])) # 7
print("-------------------------------------------", flush=True) print("-------------------------------------------", flush=True)
Vertex._num = 0 # naming reset
# exit(1) # exit(1)