diff --git a/EGE/Gen/EGE2022/N01.py b/EGE/Gen/EGE2022/N01.py new file mode 100644 index 0000000..b6456fe --- /dev/null +++ b/EGE/Gen/EGE2022/N01.py @@ -0,0 +1,394 @@ +from ... import Html as html +from ...Graph import Graph, GraphVertex +from ...GenBase import DirectInput + + +class AmbiguousTableAndGraphCorrelation(DirectInput): + graphs = [ + { + 'vertices': { + 'A': GraphVertex(at=[ 50, 0 ]), + 'B': GraphVertex(at=[ 25, 40 ]), + 'C': GraphVertex(at=[ 75, 40 ]), + 'D': GraphVertex(at=[ 50, 80 ]), + 'E': GraphVertex(at=[ 25, 105 ]), + 'F': GraphVertex(at=[ 75, 105 ]), + }, + 'edges': [ + [ 'A', 'B' ], + [ 'A', 'C' ], + [ 'B', 'D' ], + [ 'C', 'D' ], + [ 'D', 'E' ], + [ 'D', 'F' ], + [ 'E', 'F' ], + ], + 'sought_for_vertices': [ + ['B', 'C'], + ['E', 'F'], + ['A', 'D'] + ], + 'size': { + 'x': 100, + 'y': 180 + } + }, + { + 'vertices': { + 'A': GraphVertex(at=[ 25, 0 ]), + 'B': GraphVertex(at=[ 75, 0 ]), + 'C': GraphVertex(at=[ 0, 25 ]), + 'D': GraphVertex(at=[ 50, 25 ]), + 'E': GraphVertex(at=[ 100, 25 ]), + 'F': GraphVertex(at=[ 25, 50 ]), + 'G': GraphVertex(at=[ 75, 65 ]), + }, + 'edges': [ + [ 'A', 'B' ], + [ 'A', 'C' ], + [ 'A', 'D' ], + [ 'B', 'D' ], + [ 'C', 'D' ], + [ 'C', 'F' ], + [ 'F', 'D' ], + [ 'F', 'G' ], + [ 'G', 'D' ], + [ 'G', 'E' ], + [ 'E', 'D' ], + ], + 'sought_for_vertices': [ + ['B', 'E'], + ['A', 'G'], + ['C', 'F'] + ], + 'size': { + 'x': 160, + 'y': 140 + } + }, + { + 'vertices': { + 'A': GraphVertex(at=[ 25, 0 ]), + 'B': GraphVertex(at=[ 65, 0 ]), + 'C': GraphVertex(at=[ 85, 15 ]), + 'D': GraphVertex(at=[ 100, 40 ]), + 'E': GraphVertex(at=[ 85, 70 ]), + 'F': GraphVertex(at=[ 25, 70 ]), + 'G': GraphVertex(at=[ 0, 35 ]), + }, + 'edges': [ + [ 'A', 'G' ], + [ 'A', 'B' ], + [ 'A', 'F' ], + [ 'B', 'C' ], + [ 'C', 'D' ], + [ 'C', 'E' ], + [ 'D', 'E' ], + [ 'E', 'F' ], + [ 'F', 'G' ], + ], + 'sought_for_vertices': [ + ['E', 'F'], + ], + 'size': { + 'x': 160, + 'y': 140 + } + }, + { + 'vertices': { + 'A': GraphVertex(at=[ 25, 0 ]), + 'B': GraphVertex(at=[ 75, 0 ]), + 'C': GraphVertex(at=[ 0, 25 ]), + 'D': GraphVertex(at=[ 50, 25 ]), + 'E': GraphVertex(at=[ 100, 25 ]), + 'F': GraphVertex(at=[ 25, 50 ]), + 'G': GraphVertex(at=[ 75, 65 ]), + }, + 'edges': [ + [ 'A', 'B' ], + [ 'A', 'C' ], + [ 'B', 'E' ], + [ 'C', 'D' ], + [ 'C', 'F' ], + [ 'F', 'D' ], + [ 'F', 'G' ], + [ 'G', 'D' ], + [ 'G', 'E' ], + [ 'E', 'D' ], + ], + 'sought_for_vertices': [ + ['A', 'B'], + ['C', 'E'], + ['F', 'G'] + ], + 'size': { + 'x': 160, + 'y': 140 + } + }, + { + 'vertices': { + 'A': GraphVertex(at=[ 0, 0 ]), + 'B': GraphVertex(at=[ 100, 0 ]), + 'C': GraphVertex(at=[ 0, 25 ]), + 'D': GraphVertex(at=[ 25, 25 ]), + 'E': GraphVertex(at=[ 75, 25 ]), + 'F': GraphVertex(at=[ 0, 50 ]), + 'G': GraphVertex(at=[ 100, 50 ]), + }, + 'edges': [ + [ 'A', 'B' ], + [ 'A', 'C' ], + [ 'A', 'D' ], + [ 'B', 'E' ], + [ 'B', 'G' ], + [ 'C', 'D' ], + [ 'C', 'F' ], + [ 'F', 'E' ], + [ 'F', 'G' ], + [ 'G', 'E' ], + [ 'E', 'D' ], + ], + 'sought_for_vertices': [ + ['B', 'C'], + ['E', 'F'], + ['A', 'D'] + ], + 'size': { + 'x': 160, + 'y': 100 + } + }, + ] + + def _pick_graph_data(self): + graph_data = self.rnd.pick(self.graphs) + vertices = list(graph_data['vertices'].keys()) + + vertices_rename_dict = dict(zip(vertices, self.rnd.shuffle(vertices))) + graph_data['vertices'] = { vertices_rename_dict[k]: v for k, v in graph_data['vertices'].items() } + graph_data['edges'] = [ [ vertices_rename_dict[i[0]], vertices_rename_dict[i[1]]] for i in graph_data['edges'] ] + if 'sought_for_vertices' in graph_data: + graph_data['sought_for_vertices'] = [ + [vertices_rename_dict[i[0]], vertices_rename_dict[i[1]]] for i in graph_data['sought_for_vertices'] + ] + + return graph_data + + @staticmethod + def _create_graph(graph_data): + g = Graph(graph_data['vertices']) + for v1, v2 in graph_data['edges']: + g.edge2(v1, v2) + + return g + + def _get_table_vertices_names(self, g): + permutation = self.rnd.shuffle(list(range(1, len(g.vertex_names()) + 1))) + table_vertices_names = dict(zip(g.vertex_names(), permutation)) + + return table_vertices_names + + def generate(self): + graph_data = self._pick_graph_data() + g = self._create_graph(graph_data) + + table_vertices_names = self._get_table_vertices_names(g) + g_for_table = Graph({ table_vertices_names[k]: v for k, v in graph_data['vertices'].items() }) + for v1, v2 in graph_data['edges']: + g_for_table.edge2(table_vertices_names[v1], table_vertices_names[v2], '*') + + first_vertex, last_vertex = self.rnd.pick(list(graph_data['sought_for_vertices'])) + + self.text = f''' +На рисунке справа схема дорог Н-ского района изображена в виде графа, в таблице содержатся +сведения о дорогах между населенными пунктами (звездочка означает, что дорога между соответствующими городами есть). +{g_for_table.html_matrix()} {html.div_xy(g.as_svg(), graph_data['size']['x'], graph_data['size']['y'], margin='5px')} +Так как таблицу и схему рисовали независимо друг от друга, то нумерация населённых пунктов в таблице никак не связана с +буквенными обозначениями на графе. Определите номера населенных пунктов {first_vertex} и {last_vertex} в таблице. В +ответе запишите числа в порядке возрастания без разделителей.''' + + self.correct = ''.join(sorted(map(str, [ table_vertices_names[first_vertex], table_vertices_names[last_vertex] ]))) + self.accept_number() + + return self + + +class TableAndGraphCorrelation(AmbiguousTableAndGraphCorrelation): + graphs = [ + { + 'vertices': { + 'A': GraphVertex(at=[ 25, 0 ]), + 'B': GraphVertex(at=[ 75, 0 ]), + 'C': GraphVertex(at=[ 100, 25 ]), + 'D': GraphVertex(at=[ 100, 75 ]), + 'E': GraphVertex(at=[ 75, 100 ]), + 'F': GraphVertex(at=[ 25, 100 ]), + 'G': GraphVertex(at=[ 0, 50 ]), + 'H': GraphVertex(at=[ 50, 50 ]), + }, + 'edges': [ + [ 'A', 'B' ], + [ 'A', 'G' ], + [ 'A', 'H' ], + [ 'B', 'C' ], + [ 'B', 'H' ], + [ 'C', 'D' ], + [ 'D', 'E' ], + [ 'E', 'F' ], + [ 'E', 'H' ], + [ 'F', 'G' ], + [ 'G', 'H' ], + ], + 'size': { + 'x': 160, + 'y': 160 + } + }, + { + 'vertices': { + 'A': GraphVertex(at=[ 0, 50 ]), + 'B': GraphVertex(at=[ 40, 50 ]), + 'C': GraphVertex(at=[ 80, 50 ]), + 'D': GraphVertex(at=[ 100, 50 ]), + 'E': GraphVertex(at=[ 20, 0 ]), + 'F': GraphVertex(at=[ 60, 0 ]), + }, + 'edges': [ + [ 'A', 'B' ], + [ 'A', 'E' ], + [ 'B', 'E' ], + [ 'B', 'C' ], + [ 'B', 'F' ], + [ 'C', 'F' ], + [ 'C', 'D' ], + ], + 'size': { + 'x': 160, + 'y': 100 + } + }, + { + 'vertices': { + 'A': GraphVertex(at=[ 50, 0 ]), + 'B': GraphVertex(at=[ 100, 0 ]), + 'C': GraphVertex(at=[ 0, 50 ]), + 'D': GraphVertex(at=[ 50, 50 ]), + 'E': GraphVertex(at=[ 100, 50 ]), + 'F': GraphVertex(at=[ 25, 100 ]), + 'G': GraphVertex(at=[ 75, 100 ]), + }, + 'edges': [ + [ 'A', 'B' ], + [ 'A', 'C' ], + [ 'A', 'D' ], + [ 'B', 'E' ], + [ 'D', 'E' ], + [ 'C', 'D' ], + [ 'F', 'D' ], + [ 'G', 'F' ], + [ 'G', 'D' ], + ], + 'size': { + 'x': 160, + 'y': 160 + } + }, + { + 'vertices': { + 'A': GraphVertex(at=[ 0, 0 ]), + 'B': GraphVertex(at=[ 30, 0 ]), + 'C': GraphVertex(at=[ 60, 0 ]), + 'D': GraphVertex(at=[ 100, 0 ]), + 'E': GraphVertex(at=[ 0, 50 ]), + 'F': GraphVertex(at=[ 60, 50 ]), + 'G': GraphVertex(at=[ 100, 50 ]), + }, + 'edges': [ + [ 'A', 'B' ], + [ 'A', 'E' ], + [ 'B', 'C' ], + [ 'C', 'D' ], + [ 'C', 'E' ], + [ 'C', 'F' ], + [ 'C', 'G' ], + [ 'D', 'G' ], + [ 'G', 'F' ], + [ 'F', 'E' ], + ], + 'size': { + 'x': 160, + 'y': 100 + } + }, + ] + + def generate(self): + graph_data = self._pick_graph_data() + g = self._create_graph(graph_data) + + table_vertices_names = self._get_table_vertices_names(g) + g_for_table = Graph({table_vertices_names[k]: v for k, v in graph_data['vertices'].items()}) + + weights = [] + while len(weights) != len(graph_data['edges']): + weight = self.rnd.in_range(1, 20) * 5 + if weight not in weights: + weights.append(weight) + + for i in range(len(graph_data['edges'])): + g_for_table.edge2( + table_vertices_names[graph_data['edges'][i][0]], + table_vertices_names[graph_data['edges'][i][1]], + weights[i]) + + first_vertex = self.rnd.pick(list(g.vertex_names())) + last_vertex = self.rnd.pick(list(g.edges.get(first_vertex).keys())) + + self.text = f''' +На рисунке справа схема дорог Н-ского района изображена в виде графа, в таблице содержатся +сведения о длинах этих дорог (в километрах). {g_for_table.html_matrix()} +{html.div_xy(g.as_svg(), graph_data['size']['x'], graph_data['size']['y'], margin='5px')} Так как таблицу и схему +рисовали независимо друг от друга, то нумерация населённых пунктов в таблице никак не связана с буквенными обозначениями +на графе. Определите какова длина дороги из пункта {first_vertex} в пункт {last_vertex} в таблице. В ответе запишите +целое число – так, как оно указано в таблице.''' + + self.correct = g_for_table.count_path_min_weight( + table_vertices_names[first_vertex], + table_vertices_names[last_vertex]) + + self.accept_number() + + return self + + +class OptimalWayInTable(AmbiguousTableAndGraphCorrelation): + def generate(self): + graph_data = self._pick_graph_data() + + weights = [] + while len(weights) != len(graph_data['edges']): + weight = self.rnd.in_range(1, 20) * 5 + if weight not in weights: + weights.append(weight) + + g = Graph(graph_data['vertices']) + for v1, v2 in graph_data['edges']: + g.edge2(v1, v2, weights.pop()) + + first_vertex, last_vertex = self.rnd.pick_n(2, list(g.vertex_names())) + + self.text = f''' +Между населёнными пунктами {', '.join(sorted(g.vertex_names()))} построены дороги, протяжённость +которых приведена в таблице. (Отсутствие числа в таблице означает, что прямой дороги между пунктами нет.) +{g.html_matrix()} Определите длину кратчайшего пути между пунктами {first_vertex} и {last_vertex} (при +условии, что передвигаться можно только по построенным дорогам).''' + + self.correct = g.count_path_min_weight( + first_vertex, + last_vertex) + + self.accept_number() + + return self diff --git a/EGE/Gen/EGE2022/multiple.xlsx b/EGE/Gen/EGE2022/multiple.xlsx index ab17356..5e11785 100644 Binary files a/EGE/Gen/EGE2022/multiple.xlsx and b/EGE/Gen/EGE2022/multiple.xlsx differ diff --git a/EGE/Graph.py b/EGE/Graph.py index 790b14e..9f6c9b2 100644 --- a/EGE/Graph.py +++ b/EGE/Graph.py @@ -23,7 +23,7 @@ def edge1(self, v1: str, v2: str, w: int = None): self.edges[v1] = {} self.edges[v1][v2] = w - def edge2(self, v1: str, v2: str, w: int = None): + def edge2(self, v1: str, v2: str, w=None): self.edge1(v1, v2, w) if v2 not in self.edges: self.edges[v2] = {} @@ -72,12 +72,37 @@ def dfs(v: str): return dfs(src) - def html_matrix(self): + def count_path_min_weight(self, src: str, dest: str): + INF = 9999999999 + d = { i: INF for i in self.vertices } + used = { i: False for i in self.vertices } + d[src] = 0 + for i in self.vertices: + v = -1 + for j in self.vertices: + if not used[j] and (v == -1 or d[j] < d[v]): + v = j + if d[v] == INF: + break + used[v] = True + + for to, l in self.edges.get(v, {}).items(): + if d[v] + l < d[to]: + d[to] = d[v] + l + + return d[dest] + + def html_matrix(self, vertex_view_names: dict = None): vnames = sorted(self.vertex_names()) - r = html.row_n('td', [ '' ] + vnames) + if vertex_view_names: + v_view_names = [vertex_view_names[v] for v in vnames] + else: + v_view_names = vnames + r = html.row_n('td', [ '' ] + v_view_names) for v in vnames: e = self.edges.get(v, {}) - r += html.row_n('td', [ v ] + [ e.get(v, ' ') for v in vnames ]) + v_view_name = vertex_view_names[v] if vertex_view_names else v + r += html.row_n('td', [ v_view_name ] + [ e.get(v, ' ') for v in vnames ]) return html.tag('table', r, border=1) def bounding_box(self): diff --git a/gen_2022.py b/gen_2022.py index 3157de7..b106de2 100644 --- a/gen_2022.py +++ b/gen_2022.py @@ -4,11 +4,14 @@ import EGE.Html import EGE.Random -from EGE.Gen.EGE2022 import N03, N05, N06, N07, N08, N11, N14 +from EGE.Gen.EGE2022 import N01, N03, N05, N06, N07, N08, N11, N14 rnd = EGE.Random.Random(2342134) questions = [q.generate() for q in [ + N01.AmbiguousTableAndGraphCorrelation(rnd), + N01.TableAndGraphCorrelation(rnd), + N01.OptimalWayInTable(rnd), N03.GenDatabase(rnd), N05.FindNumber(rnd), N05.MinAddDigits(rnd),