Анализ данных · Блок 27ЕГЭ 2026
SilverTests · Подготовка к ЕГЭКластеризация звёзд · полный курс
Кластеризация звёзд
Задание 27 ЕГЭ-2026 по информатике · от первой диаграммы до досрочного 2026 с параметрами звёзд
1Что просят сделать
На плоскости даны точки-звёзды. Их надо разбить на N непересекающихся кластеров (каждый помещается в прямоугольник H×W), для каждого найти центр-медоид и посчитать какую-то функцию от центров.
| Файл |
Кластеров |
Размер |
Что даёт |
| A |
2 |
≤ 1000 точек |
1 балл |
| B (Б) |
3 |
≤ 10 000 точек |
+1 балл |
Медоид — ключевой термин
Медоид кластера — это одна из точек кластера, у которой сумма расстояний до всех остальных минимальна.
Это НЕ среднее координат! Медоид всегда совпадает с одной из исходных звёзд. В Python: min(c, key=lambda p: sum(dist(p, q) for q in c)).
2Главный приём: глаз + Excel + Python
На станции КЕГЭ установлен LibreOffice Calc — это разрешённый инструмент. Кластеризацию делает не алгоритм, а твой глаз по точечной диаграмме. Python нужен только для медоида и подсчёта ответа.
ШАГ 1
Excel
Открыть файл, построить точечную диаграмму
ШАГ 2
Разделение
Глазами увидеть облака. Снять границы (x<5, y>3)
ШАГ 3
Python
Фильтр по условию, медоид каждого кластера
ШАГ 4
Формат
Считаем, что просит условие, выводим строго
3Шаг 1 в коде: как прочитать файл
Файл выглядит просто — в каждой строке координаты одной звезды через пробел, десятичный разделитель — запятая:
27_A.txt2,73 4,15
3,18 4,72
2,91 3,88
...
Чтобы получить из этих строк список пар чисел, нужно сделать 5 простых операций над каждой строкой. Сначала покажу развёрнутый вариант через обычный цикл — в нём видно, что происходит на каждом шаге:
развёрнутоpts = [] # пустой список точек
with open('27_A.txt', encoding='utf-8') as f:
for line in f: # шаг 1: читаем по строкам
line = line.strip() # шаг 2: убираем переносы '\n'
if not line: # и пропускаем пустые строки
continue
line = line.replace(',', '.') # шаг 3: запятая → точка
parts = line.split() # шаг 4: '2.73 4.15' → ['2.73', '4.15']
x = float(parts[0]) # шаг 5: строка → число
y = float(parts[1])
pts.append((x, y)) # кладём кортеж (x, y) в список
А теперь то же самое одной строкой через list comprehension. Идея ровно та же, просто записана компактнее:
компактноwith open('27_A.txt', encoding='utf-8') as f:
pts = [tuple(map(float, l.replace(',', '.').split()))
for l in f]
Что делает каждая часть компактной записи
| Кусочек |
Что делает |
l.replace(',', '.') |
заменяет запятые на точки в строке l |
.split() |
делит строку по пробелам: '2.73 4.15' → ['2.73', '4.15'] |
map(float, ...) |
превращает каждую строку списка в число: ['2.73', '4.15'] → [2.73, 4.15] |
tuple(...) |
склеивает в кортеж: (2.73, 4.15) |
[... for l in f] |
повторяет это для каждой строки файла |
if l.strip() |
отбрасывает пустые строки в конце файла |
А если в файле 3 столбца? (досрочный 2026)
В досрочном 2026 в каждой строке три значения: x y type. Тогда чтение совсем чуть-чуть длиннее:
3 столбцаstars = []
with open('27_A.txt', encoding='utf-8') as f:
for line in f:
line = line.strip().replace(',', '.')
if not line: continue
x, y, type_s = line.split() # распаковка в 3 переменные
stars.append((float(x), float(y), type_s))
Главное правило: сразу после чтения распечатай первые 3 точки — print(pts[:3]). Если видишь правильные пары чисел — значит, всё прочиталось. Если ValueError — забыл replace(',', '.').
4Интерактив: файл A (2 кластера, деление по X)
Слева — таблица координат (как в файле 27_A.txt). Справа — диаграмма. Двигай ползунок — точки в реальном времени перекрашиваются. Включи кнопку «медоиды» — увидишь центры кластеров.
Файл A — 2 кластераSIM · LIVE
Граница x =5.0
Попробуй: поставь границу на x = 1.5 — почти все точки в одном кластере. Правильное разделение — в районе x ≈ 5, где между облаками пустой зазор.
5Интерактив: файл Б (3 кластера, две границы)
Для файла Б нужно три кластера — значит, две разделяющие прямые. Двигай оба ползунка.
Файл Б — 3 кластераSIM · LIVE
6Медоид: разбираем функцию по косточкам
В интерактивах ты видел кнопку «медоиды» — и центры кластеров действительно оказывались на одной из точек. Сейчас разберём, какой код это делает. Вот компактная запись функции:
компактноdef medoid(c):
return min(c, key=lambda p: sum(dist(p, q) for q in c))
В одной строке тут спрятано четыре идеи: min, key, lambda и сумма расстояний. Разберём по очереди.
Что значит каждый кусок
| Кусочек |
Что делает |
c |
список точек кластера, для которого ищем центр |
min(c, key=...) |
находит точку из c, у которой значение функции key минимально |
lambda p: ... |
безымянная функция: «дай мне точку p, я верну тебе число» |
sum(dist(p, q) for q in c) |
складывает dist(p, q1) + dist(p, q2) + ... + dist(p, qN) |
То же самое через обычный цикл
Если min, key и lambda кажутся магией — вот тот же алгоритм через обычный цикл, без single-line магии. Делает ровно то же самое:
через циклdef medoid(c):
best = c[0] # лучшая точка (пока — первая)
best_sum = 10**18 # минимальная сумма (пока — «бесконечность»)
for p in c: # перебираем все точки кластера
s = 0 # сумма расстояний от p до остальных
for q in c:
s = s + dist(p, q)
if s < best_sum: # нашли точку с меньшей суммой —
best_sum = s # запомнили её
best = p
return best # точка с минимальной суммой расстояний
Как это работает на примере
Пусть кластер из 4 точек: A=(0,0), B=(1,0), C=(2,0), D=(3,0). Считаем суммы расстояний от каждой точки до остальных:
| Точка |
Расстояния до остальных |
Сумма |
| A (0,0) |
1 + 2 + 3 |
6 |
| B (1,0) |
1 + 1 + 2 |
4 — минимум! |
| C (2,0) |
2 + 1 + 1 |
4 — тоже минимум |
| D (3,0) |
3 + 2 + 1 |
6 |
Видим: точки B и C «равноправно центральные». min вернёт ту, которая встретилась первой. В реальной задаче ФИПИ гарантирует единственность медоида — такого равенства не будет.
Какой вариант писать на ЕГЭ? Оба работают одинаково. Если уверен в lambda и min — пиши компактно (3 строки). Если боишься запутаться — пиши развёрнуто через цикл (12 строк). На скорость не влияет, на балл тоже.
7Когда по X не работает: выбор направления
Облака могут лежать не только слева-справа. Тогда нужно выбрать правильное направление разделителя. В интерактиве ниже: сверху выбираешь расположение данных, снизу — направление. Только одна комбинация работает для каждого расположения.
Выбор направления разделителяSIM · SWITCHABLE
Расположение:
Делю по оси:
Граница x =5.0
| Вижу на диаграмме |
Какое условие пишу |
Тип зазора |
| Облака слева ↔ справа |
if p[0] < A: c1 else: c2 |
вертикальный (по x) |
| Облака сверху ↔ снизу |
if p[1] < B: c1 else: c2 |
горизонтальный (по y) |
| Облака по диагонали |
if p[0] < A and p[1] > B |
составное условие |
| Облако внутри круга |
if (x-x₀)**2+(y-y₀)**2 < r**2 |
облако-кольцо |
| Облака под наклоном |
if 2*x + 3*y < 12 |
диагональная линия |
8L-образное деление: 3 кластера «треугольником»
Когда облака стоят треугольником (одно сверху, два внизу), нужны две границы: одна горизонтальная для верхнего, одна вертикальная для нижних. Классическая L-образная конфигурация.
паттернif p[1] > 5: # y > 5 — верхнее облако
c1.append(p)
elif p[0] < 5: # из оставшихся: x < 5 — левое нижнее
c2.append(p)
else: # всё прочее — правое нижнее
c3.append(p)
3 кластера — L-образная границаSIM · 2D-SPLIT
Верх y =5.0
Низ x =5.0
9Выбросы — убираем глазами
В части вариантов (ЕГЭ-2025, резервы) среди обычных точек прячутся выбросы — одинокие звёзды далеко от любого облака. На диаграмме они сразу заметны. Кликни по точке (или строке в таблице) — пометишь её как выброс. Она перестанет участвовать в медоиде.
2 кластера + выбросы (клик — пометить/снять)SIM · CLICKABLE
Граница x =5.0
Кнопка «подсветить выбросы» сама находит точки с ближайшим соседом дальше 2 единиц — именно это видит глаз.
10Досрочный 2026: характеристики звёзд
В досрочном ЕГЭ-2026 (7 апреля 2026) в задание 27 добавили третий столбец — характеристику звезды, записанную слитно: цвет (буква) + светимость (цифра) + размер (римская цифра).
пример строки2,73 4,15 G2II
| | |||
x y цвет свет. размер
G — белый, 2 — светимости 2, II — карлик
# итого: белый карлик светимости 2
Таблица обозначений
| Цвет (1 буква) |
Размер (римская) |
G |
белый |
I |
бланкет |
J |
зелёный |
II |
карлик |
L |
синий |
III |
гигант |
N |
оранжевый |
IV |
сверхгигант |
Y |
красный |
V |
мега-гигант |
S |
голубой |
VI |
супер-гигант |
Z |
жёлтый |
VII |
пухляк |
Как разобрать характеристику в Python:
x, y, type_s = line.replace(',', '.').split()
x, y = float(x), float(y)
color = type_s[0] # 1-я буква — цвет, напр. 'G'
bright = type_s[1] # 2-й символ — цифра, напр. '2'
size = type_s[2:] # всё после — размер, напр. 'II'
В задаче часто спрашивают про
цвет + размер:
'GI' (белый бланет),
'NII' (оранжевый карлик),
'YIII' (красный гигант),
'ZIV' (жёлтый сверхгигант).
Легенда цветов
G — белый J — зелёный L — синий N — оранжевый Y — красный S — голубой Z — жёлтый
Радиус точки отражает «размер» звезды: I (бланкет) — маленький, VII (пухляк) — большой.
Файл A досрочного 2026 (звёзды разных типов)SIM · PARAMS
Граница y =5.0
Цвет+Размер:
Выбранный тип звёзд подсвечивается золотым контуром. При включении медоидов от центра большого кластера к подсвеченным звёздам тянутся золотые пунктиры; в статистике появляются A1 и A2.
11Полные коды решений
Базовая задача: средние координат центров (досрочный 2025)
базовыйfrom math import dist
with open('27_A.txt', encoding='utf-8') as f:
pts = [tuple(map(float, l.replace(',', '.').split()))
for l in f if l.strip()]
c1 = [p for p in pts if p[0] < 5] # условие с диаграммы
c2 = [p for p in pts if p[0] >= 5]
def medoid(c):
return min(c, key=lambda p: sum(dist(p, q) for q in c))
cen1, cen2 = medoid(c1), medoid(c2)
Px = (cen1[0] + cen2[0]) / 2
Py = (cen1[1] + cen2[1]) / 2
print(abs(int(Px * 10000)), abs(int(Py * 10000)))
# Эталон файла A досрочного 2025: 167990 73043
Досрочный 2026: звёзды с параметрами (файл A)
досрочный'26from math import dist
stars = []
with open('27_A.txt', encoding='utf-8') as f:
for line in f:
line = line.strip().replace(',', '.')
if not line: continue
x, y, type_s = line.split()
stars.append((float(x), float(y),
type_s[0], type_s[1], type_s[2:]))
c1 = [s for s in stars if s[1] > 10] # верхнее облако
c2 = [s for s in stars if s[1] <= 10] # нижнее облако
big = max([c1, c2], key=len) # самый большой кластер
def medoid(c):
return min(c, key=lambda p: sum(dist((p[0],p[1]),(q[0],q[1]))
for q in c))
center = medoid(big)
# Расстояния от центра до «белых бланетов» (GI) — со всех точек!
dists = []
for s in stars:
if s[2] == 'G' and s[4] == 'I':
if (s[0], s[1]) != (center[0], center[1]):
dists.append(dist((s[0],s[1]), (center[0],center[1])))
A1, A2 = min(dists), max(dists)
print(int(abs(A1 * 10000)), int(abs(A2 * 10000)))
# Эталон: 5522 75202
12Чек-лист перед отправкой ответа
| № |
Что проверить |
| 1 |
Построил точечную диаграмму в Excel/Calc |
| 2 |
Увидел нужное число облаков (2 для A, 3 для Б) |
| 3 |
Граница прошла по пустому зазору |
| 4 |
Выбрал правильное направление разделителя (по x или по y) |
| 5 |
len(c1) + len(c2) [+ len(c3)] == len(pts) |
| 6 |
Центр = медоид, не среднее координат |
| 7 |
Использовал int(), а не round() |
| 8 |
Применил abs(), если требуется формулой |
| 9 |
Не перепутал порядок чисел в ответе (Pₓ↔P_y, P1↔P2, Q1↔Q2) |
| 10 |
Если есть выбросы — отсеял (if/elif без else) |
| 11 |
Если есть характеристика — разобрал её правильно (size = type_s[2:]) |
| 12 |
Перечитал условие ещё раз |