Подтема 5 из 6
Расстояния и пары точек
Радиус, расстояния до точек своего и чужого кластера, диаметр, межкластерный диаметр, точки около центра, объекты нужного вида и задача темы.
Радиус
Как найти, насколько далеко от центра разбросаны точки кластера?
Радиус кластера — расстояние от центра до самой далёкой точки своего кластера. Возьмём кластеры файла example27.txt: small — правый из трёх точек, big — левый из пяти. Функция center_sum — из подтемы 3:
small = [(10, 0), (12, 0), (11, 1)]big = [(0, 0), (0, 2), (1, 1), (2, 0), (2, 2)]c_small, c_big = center_sum(small), center_sum(big) r_small = max(dist(c_small, p) for p in small)r_big = max(dist(c_big, p) for p in big)print(r_small, r_big) far = max(dist(c_small, p) for p in big)print(far)Радиусы обоих кластеров — √2 ≈ 1,414: от центров (11; 1) и (1; 1) до самых далёких точек — по диагонали клетки. Последнее число — уже не радиус: это расстояние от центра малого кластера до самой далёкой точки чужого, большого кластера — до (0; 0) или (0; 2). Перед каждым расчётом реши два вопроса: от чего до чего считаем и среди каких точек выбираем.
Центр ищут один раз, до цикла. В записи max(dist(center_sum(cluster), p) for p in cluster) поиск центра повторится для каждой точки, и программа, которая работала секунду, будет работать часами.
Диаметр кластера
Как найти наибольшее расстояние между двумя точками кластера?
Перебрать все пары точек кластера — каждую один раз — и запомнить наибольшее расстояние. Это диаметр кластера. Определение, как всегда, сверяй с условием: D = max d(A, B), где A и B — точки одного кластера.
def diameter_of(cluster): diameter = 0.0 for i in range(len(cluster)): for j in range(i + 1, len(cluster)): diameter = max( diameter, dist(cluster[i], cluster[j]) ) return diameterprint(diameter_of(small), diameter_of(big))Внутренний цикл начинается с i + 1: каждая пара проверяется один раз, а точка не сравнивается сама с собой.
Если требуется не только расстояние, но и координаты точек, образующих диаметр, нужно запоминать найденную пару. У левого кластера диаметр 2√2 дают сразу две пары — две диагонали квадрата; поэтому в задачах, где важны концы диаметра, условие гарантирует, что пара единственная.
Диаметр не обязан совпадать с расстоянием от выбранного центра до самой далёкой точки. Если известен центр и его радиус R, то диаметр не больше 2R, но равенство выполняется не всегда. Поэтому автоматически писать diameter = 2 * radius нельзя. У правого кластера радиус √2 ≈ 1,414, а диаметр 2 — меньше, чем 2√2.
Межкластерный диаметр
Как найти самые далёкие друг от друга точки двух разных кластеров?
Брать по одной точке из каждого кластера и запоминать не только наибольшее расстояние, но и саму пару. Это межкластерный диаметр; в задачах часто что-то считают и по его концам. Запусти функцию по шагам: сначала лучшая пара даёт 12, затем ≈ 12,166.
from math import dist def farthest_pair(cluster1, cluster2): best_distance = -1.0 best_pair = None for point1 in cluster1: for point2 in cluster2: d = dist(point1, point2) if d > best_distance: best_distance = d best_pair = (point1, point2) return best_distance, best_pair print(farthest_pair([(0, 0), (0, 2)], [(12, 0)]))— специальное значение, означающее, что результат пока не установлен. Оба кластера здесь предполагаются непустыми, поэтому пара обязательно будет найдена. Функция возвращает , их раскладывают по именам — и теперь можно вычислить, например, модуль разности абсцисс концов и сумму их ординат:
diameter, pair = farthest_pair(small, big)point1, point2 = pair x_difference = abs(point1[0] - point2[0])y_sum = point1[1] + point2[1]print(diameter, pair, x_difference, y_sum)Если кластеров три, межкластерных диаметров тоже три — по одному на каждую пару кластеров. Пары номеров перебирают так же, как пары точек, — с i + 1:
clusters = [small, big]total = 0.0for i in range(len(clusters)): for j in range(i + 1, len(clusters)): d, pair = farthest_pair(clusters[i], clusters[j]) total += d print(total)Здесь кластеров два, и сумма состоит из одного диаметра.
Если требуется минимальное расстояние между кластерами, организация похожая, но начальное значение — положительная бесконечность, а сравнение — <.
Расстояние между центрами не заменяет расстояния между крайними или ближайшими точками кластеров. Здесь расстояние между центрами — 10, а межкластерный диаметр ≈ 12,166.
И ещё: когда нужно найти минимальное расстояние между различными точками одного кластера, нельзя включать пару точки с самой собой. Иначе минимум всегда получится нулевым. Для различения записей надёжнее использовать разные индексы. Проверка point1 != point2 исключит также разные записи с одинаковыми координатами.
Точки около центра
Как посчитать точки на расстоянии не больше r от центра?
Сначала точно определить, среди каких точек считаем и строгое ли неравенство, — и только потом писать цикл. Подсчёт выглядит так:
count = 0 for point in points: if dist(center, point) <= r: count += 1Фраза «на плоскости» может означать все допустимые точки файла. Фраза «этого кластера» — только точки соответствующего кластера: for point in cluster:. Нельзя менять область перебора по привычке. Слова в условии переводятся буквально:
- меньше r
dist(center, point) < r- не больше r
dist(center, point) <= r- больше r
dist(center, point) > r- не меньше r
dist(center, point) >= r
Если центр является одной из перебираемых точек, его расстояние до себя равно нулю. При условии <= r и неотрицательном r он будет посчитан. Это правильно, если условие включает центр. Если требуется считать только остальные точки, его нужно исключить как конкретную запись.
Посчитаем для центра (1; 1) и r = 10 все три варианта:
points = small + bigcenter = (1, 1) print(sum(1 for p in points if dist(center, p) <= 10))print(sum(1 for p in points if dist(center, p) < 10))print(sum(1 for p in big if dist(center, p) <= 10))Те же точки и окружность на рисунке:
«На плоскости, не больше 10» — 7 точек: к пяти точкам своего кластера добавились (10; 0) и (11; 1). «Меньше 10» — 6: точка (11; 1) на самой окружности выпала. «Этого кластера» — 5.
Объекты нужного вида
Как найти ближайшую к центру звезду нужного вида или пару таких звёзд?
Сначала отобрать объекты нужного вида, потом искать среди них — внутри одного кластера; центр при этом ищут по всем точкам кластера. В задачах ЕГЭ-2026 спрашивают, например, координаты ближайшего к центру красного гиганта или расстояние между двумя жёлтыми карликами одного кластера. Найдём белый карлик, ближайший к центру самого большого кластера файла 27-3.txt. Функция read_stars — из подтемы 1, split_stars — из подтемы 4:
clusters = split_stars(read_stars("27-3.txt"))big = max(clusters, key=len)center = center_sum([s[:2] for s in big]) dwarfs = [s for s in big if s[2] == "VII"]nearest = min(dwarfs, key=lambda s: dist(center, s[:2])) print(center, len(dwarfs))print(nearest, dist(center, nearest[:2]))Центр ищем по координатам s[:2] — по всем звёздам кластера, а не только по белым карликам.
max(clusters, key=len) возвращает сам кластер с наибольшим числом точек, min(dwarfs, key=…) — сам ближайший объект, а не расстояние до него; запись lambda s: … — короткая функция для ключа. Если нужен вид, который определяется буквой и светимостью, отбор пишут через parse_kind.
Пары объектов одного вида внутри одного кластера перебирают по индексам, как пары точек для диаметра. Наименьшее расстояние между двумя разными белыми карликами одного кластера:
best = float("inf") for cluster in clusters: dw = [s[:2] for s in cluster if s[2] == "VII"] for i in range(len(dw)): for j in range(i + 1, len(dw)): best = min(best, dist(dw[i], dw[j])) print(best)Пары из разных кластеров сюда не попадают: список dw собирается заново для каждого кластера.
Задача темы
Вернёмся к задаче темы:
«Входной файл 27-4.txt содержит сведения о частицах, зарегистрированных детектором. В каждой строке записаны координаты частицы x и y, проекции её скорости vx и vy, масса m и признак частицы — римское число от I до VII; значения разделены табуляцией. Кинетическая энергия частицы E = m · (vx² + vy²) / 2. Частицы образуют четыре кластера: модуль разности энергий любых двух частиц одного кластера не превышает 2. Центром кластера назовём частицу, для которой сумма модулей разностей её энергии и энергий остальных частиц кластера минимальна. Найдите Q₁ — наибольшее расстояние между двумя частицами одного кластера с признаком II и Q₂ — наибольшую кинетическую энергию центра кластера. В ответе запишите целые части произведений Q₁ · 10 000 и Q₂ · 10 000.»
Все шаги уже разобраны: прочитать частицы (подтема 1), разделить по энергии (подтема 2), найти центры по разности энергий (подтема 3) и посчитать два ответа на одних и тех же кластерах. Q₁ — наибольшее расстояние между частицами с признаком II внутри одного кластера — это пары, как у диаметра, только среди отобранных частиц. Q₂ — наибольшая энергия центра.
from math import dist def read_particles(filename): particles = [] with open(filename, encoding="utf-8-sig") as f: for line in f: parts = line.replace(",", ".").split() x, y, vx, vy, m = [float(v) for v in parts[:5]] particles.append((x, y, vx, vy, m, parts[5])) return particles def energy(p): return p[4] * (p[2] ** 2 + p[3] ** 2) / 2 def energy_gap(a, b): return abs(energy(a) - energy(b)) def center_sum(cluster, d=dist): best_point = cluster[0] best_sum = float("inf") for candidate in cluster: total = 0.0 for point in cluster: total += d(candidate, point) if total < best_sum: best_sum = total best_point = candidate return best_point particles = read_particles("27-4.txt")bounds = [3.5, 7, 10.5]groups = [[] for _ in range(len(bounds) + 1)]for p in particles: groups[sum(energy(p) > b for b in bounds)].append(p) q1 = 0.0for g in groups: tagged = [p[:2] for p in g if p[5] == "II"] for i in range(len(tagged)): for j in range(i + 1, len(tagged)): q1 = max(q1, dist(tagged[i], tagged[j])) q2 = max(energy(center_sum(g, energy_gap)) for g in groups)print(int(q1 * 10000), int(q2 * 10000))Ответ: 772010 125026. Новое в программе только одно место — номер группы: это число границ, которые энергия частицы превысила, а sum считает истинные сравнения как единицы. Файл задачи — 27-4.txt.
Q₁ считают по координатам — p[:2], хотя кластеры и центры выбирали по энергии. А список частиц с признаком II собирают заново в каждом кластере: пары из разных кластеров в Q₁ не входят.
Итого
- Радиусот центра до самой далёкой точки своего кластера
- Центрищут один раз, до цикла по точкам
- Диаметрпары с
jотi + 1; не обязан быть 2R - Межкластерныйпо точке из двух кластеров; запоминай и пару
- В радиусесреди каких точек и строгое ли неравенство
- Нужный видсначала отбор, потом поиск — внутри кластера
Проверьте себя
Ответь сначала сам, потом открой ответ.
ОтветСкрыть ответ
Почему следующий код может быть очень медленным?
for point in cluster: d = dist(center_sum(cluster), point)ОтветСкрыть ответ
(0, 0), (4, 0), (4, 3). Найди его центр, радиус и диаметр. Равен ли диаметр удвоенному радиусу?ОтветСкрыть ответ
(4, 0) (суммы: 9, 7 и 8), радиус 4, диаметр 5 — между (0, 0) и (4, 3); 5 меньше 2 · 4 = 8.(1, 1), (1, 1), (3, 1). Чему равно минимальное расстояние между разными записями? А если пары отбирать проверкой point1 != point2?ОтветСкрыть ответ
point1 != point2 пара одинаковых координат пропускается, и получится 2 — это уже другой вопрос.Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Центры и радиус
В файле 27-2.txt записаны точки трёх кластеров и ровно три аномалии, которые не относятся ни к одному кластеру. Найди Q₁ — расстояние между центрами кластеров с наименьшим и наибольшим количеством точек и Q₂ — наибольшее расстояние от центра кластера до точки этого же кластера среди всех кластеров. В ответе запиши целые части произведений Q₁ · 10 000 и Q₂ · 10 000.
Введите два целых числа через пробел.
Подсказка
Q₂ — расстояния от центра только до точек своего кластера.
Решение
105036 28355.
Решение программой
from math import dist def read_points(filename): points = [] with open(filename, encoding='utf-8-sig') as f: for line in f: parts = line.replace(',', '.').split() points.append((float(parts[0]), float(parts[1]))) return points def center_sum(cluster): best_point = cluster[0] best_sum = float('inf') for candidate in cluster: total = 0.0 for point in cluster: total += dist(candidate, point) if total < best_sum: best_sum = total best_point = candidate return best_point clusters = [[], [], []]for x, y in read_points('27-2.txt'): if -8 < x < 0 and 2 < y < 10: clusters[0].append((x, y)) elif 1 < x < 10 and 2 < y < 10: clusters[1].append((x, y)) elif -2 < x < 5 and -7 < y < 1: clusters[2].append((x, y))clusters.sort(key=len)centers = [center_sum(c) for c in clusters] q1 = dist(centers[0], centers[-1])q2 = 0for c, cluster in zip(centers, clusters): for p in cluster: q2 = max(q2, dist(c, p))print(int(q1 * 10000), int(q2 * 10000))Ответ: 105036 28355
Центры и жёлтые карлики
Для того же файла найди B₁ — расстояние между центрами кластеров с наименьшим и наибольшим количеством красных гигантов и B₂ — наибольшее расстояние между двумя жёлтыми карликами (класс G, светимость V) одного кластера. В ответе запиши целые части произведений B₁ · 10 000 и B₂ · 10 000.
Введите два целых числа через пробел.
Подсказка
B₂ — пары жёлтых карликов внутри одного кластера.
Решение
107579 49310.
Решение программой
from math import dist def center_sum(cluster): best_point = cluster[0] best_sum = float('inf') for candidate in cluster: total = 0.0 for point in cluster: total += dist(candidate, point) if total < best_sum: best_sum = total best_point = candidate return best_point stars = []with open('27-3.txt', encoding='utf-8-sig') as f: for line in f: sx, sy, code = line.replace(',', '.').split() stars.append((float(sx), float(sy), code)) clusters = [[], [], []]for s in stars: if s[1] < 5: clusters[0].append(s) elif s[0] < 10: clusters[1].append(s) else: clusters[2].append(s) def is_kind(code, cls, lum): return code[0] == cls and code[2:] == lum giants = [sum(is_kind(s[2], 'M', 'III') for s in c) for c in clusters]centers = [center_sum([(s[0], s[1]) for s in c]) for c in clusters]lo = giants.index(min(giants))hi = giants.index(max(giants))b1 = dist(centers[lo], centers[hi])b2 = 0for c in clusters: g = [(s[0], s[1]) for s in c if is_kind(s[2], 'G', 'V')] for i in range(len(g)): for j in range(i + 1, len(g)): b2 = max(b2, dist(g[i], g[j]))print(int(b1 * 10000), int(b2 * 10000))Ответ: 107579 49310
Расстояния между кластерами
Первый кластер: (0, 0), (1, 0). Второй: (4, 0), (6, 0). Найди минимальное и максимальное расстояния между точками разных кластеров.
Введите два числа через пробел в том порядке, в каком их спрашивают.
Подсказка
Бери по одной точке из каждого кластера.
Решение
Минимальное — 3, максимальное — 6.
Решение программой
from math import dist first = [(0, 0), (1, 0)]second = [(4, 0), (6, 0)]d = [dist(a, b) for a in first for b in second]print(f'{min(d):g} {max(d):g}')Ответ: 3 6
Точки около центра
Точки (0, 0), (1, 0), (3, 0), (4, 0), (10, 0), центр — (1, 0). Сколько точек на расстоянии не больше 3 от центра, включая сам центр? Сколько на расстоянии меньше 3? Сколько **остальных** точек на расстоянии не больше 3?
Введите три числа через пробел в том порядке, в каком их спрашивают.
Подсказка
«Не больше» включает границу; «остальные» — без самого центра.
Решение
4 (сам центр, (0, 0), (3, 0), (4, 0)); 3 ((4, 0) ровно на расстоянии 3 выпадает); 3 остальных.
Решение программой
from math import dist points = [(0, 0), (1, 0), (3, 0), (4, 0), (10, 0)]c = (1, 0)near = [p for p in points if dist(c, p) <= 3]print(len(near), sum(dist(c, p) < 3 for p in points), len([p for p in near if p != c]))Ответ: 4 3 3
Ближайший красный гигант
В файле 27-3.txt записаны звёзды трёх кластеров: координаты и характеристика (спектральный класс, подкласс, класс светимости). Центр кластера — его звезда с наименьшей суммой расстояний до остальных звёзд кластера. Найди A₁ и A₂ — абсциссу и ординату красного гиганта (класс M, светимость III), ближайшего к центру кластера с наименьшим количеством звёзд. В ответе запиши целые части произведений A₁ · 10 000 и A₂ · 10 000.
Введите два целых числа через пробел.
Подсказка
Центр находят по всем звёздам кластера, а ближайшего ищут среди красных гигантов.
Решение
150226 81623.
Решение программой
from math import dist def center_sum(cluster): best_point = cluster[0] best_sum = float('inf') for candidate in cluster: total = 0.0 for point in cluster: total += dist(candidate, point) if total < best_sum: best_sum = total best_point = candidate return best_point stars = []with open('27-3.txt', encoding='utf-8-sig') as f: for line in f: sx, sy, code = line.replace(',', '.').split() stars.append((float(sx), float(sy), code)) clusters = [[], [], []]for s in stars: if s[1] < 5: clusters[0].append(s) elif s[0] < 10: clusters[1].append(s) else: clusters[2].append(s) def is_kind(code, cls, lum): return code[0] == cls and code[2:] == lum small = min(clusters, key=len)c = center_sum([(s[0], s[1]) for s in small])giants = [s for s in small if is_kind(s[2], 'M', 'III')]near = min(giants, key=lambda s: dist(c, (s[0], s[1])))print(int(near[0] * 10000), int(near[1] * 10000))Ответ: 150226 81623
Задача темы: частицы
Задача темы. Входной файл 27-4.txt содержит сведения о частицах, зарегистрированных детектором. В каждой строке записаны координаты частицы x и y, проекции её скорости vx и vy, масса m и признак частицы — римское число от I до VII; значения разделены табуляцией. Кинетическая энергия частицы E = m · (vx² + vy²) / 2. Частицы образуют четыре кластера: модуль разности энергий любых двух частиц одного кластера не превышает 2. Центром кластера назовём частицу, для которой сумма модулей разностей её энергии и энергий остальных частиц кластера минимальна. Найди Q₁ — наибольшее расстояние между двумя частицами одного кластера с признаком II и Q₂ — наибольшую кинетическую энергию центра кластера. В ответе запиши целые части произведений Q₁ · 10 000 и Q₂ · 10 000.
Введите два числа через пробел в том порядке, в каком их спрашивают.
Подсказка
Q₁ — расстояние по координатам между частицами с признаком II внутри одного кластера; Q₂ — энергия центра, найденного по разностям энергий.
Решение
772010 125026.
Решение программой
from math import dist parts = []with open('27-4.txt', encoding='utf-8-sig') as f: for line in f: p = line.replace(',', '.').split() parts.append([float(v) for v in p[:5]] + [p[5]]) def energy(p): return p[4] * (p[2] ** 2 + p[3] ** 2) / 2 clusters = [[], [], [], []]for p in parts: k = 0 for bound in [3.5, 7, 10.5]: if energy(p) > bound: k += 1 clusters[k].append(p) def center_energy(cluster): best, best_sum = cluster[0], float('inf') for c in cluster: total = 0.0 for p in cluster: total += abs(energy(c) - energy(p)) if total < best_sum: best, best_sum = c, total return best q1 = 0for c in clusters: two = [p for p in c if p[5] == 'II'] for i in range(len(two)): for j in range(i + 1, len(two)): d = dist(two[i][:2], two[j][:2]) q1 = max(q1, d)q2 = max(energy(center_energy(c)) for c in clusters)print(int(q1 * 10000), int(q2 * 10000))Ответ: 772010 125026