Solingen

lab2.py

Apr 9th, 2026
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.77 KB | None | 0 0
  1. import math
  2.  
  3. # ============================================================
  4. # Матрица косинусного подобия
  5. # ============================================================
  6. # Здесь задаём входные данные. Если хотите подставить свою матрицу —
  7. # просто поменяйте labels_init и raw_sim, главное чтобы метки совпадали.
  8.  
  9. labels_init = ['U1', 'U2', 'U3', 'U4', 'U5']
  10.  
  11. # Задаём только верхний треугольник — матрица симметричная,
  12. # поэтому повторяться не нужно
  13. raw_sim = {
  14.     ('U1', 'U2'): 0.95,
  15.     ('U1', 'U3'): 0.90,
  16.     ('U1', 'U4'): 0.85,
  17.     ('U1', 'U5'): 0.80,
  18.     ('U2', 'U3'): 0.75,
  19.     ('U2', 'U4'): 0.70,
  20.     ('U2', 'U5'): 0.65,
  21.     ('U3', 'U4'): 0.60,
  22.     ('U3', 'U5'): 0.80,
  23.     ('U4', 'U5'): 0.70,
  24. }
  25.  
  26. # Порог: если подобие двух кластеров ниже R, объединять их уже не будем
  27. R = 0.85
  28.  
  29. # ============================================================
  30. # Вспомогательные функции
  31. # ============================================================
  32.  
  33. def build_matrix(labels, raw):
  34.     """Разворачиваем половинчатый словарь в полную матрицу подобий."""
  35.     sim = {}
  36.     for i in labels:
  37.         for j in labels:
  38.             if i == j:
  39.                 sim[(i, j)] = 1.0
  40.             elif (i, j) in raw:
  41.                 sim[(i, j)] = raw[(i, j)]
  42.             elif (j, i) in raw:
  43.                 sim[(i, j)] = raw[(j, i)]
  44.             else:
  45.                 sim[(i, j)] = 0.0
  46.     return sim
  47.  
  48. def print_matrix(labels, sim, title):
  49.     """Красиво печатаем матрицу подобия в консоль."""
  50.     col_w = 12
  51.     lbl_w = 16
  52.  
  53.     print(f"\n  {title}")
  54.     header = " " * lbl_w + "".join(f"{l:>{col_w}}" for l in labels)
  55.     print("  " + header)
  56.     print("  " + "-" * len(header))
  57.     for i in labels:
  58.         row = f"{i:<{lbl_w}}"
  59.         for j in labels:
  60.             # Нижний треугольник не дублируем — ставим прочерк
  61.             if labels.index(i) > labels.index(j):
  62.                 row += f"{'—':>{col_w}}"
  63.             else:
  64.                 row += f"{sim[(i,j)]:>{col_w}.6f}"
  65.         print("  " + row)
  66.  
  67. def find_max_pair(labels, sim):
  68.     """
  69.    Ищем пару кластеров с наибольшим подобием — именно их будем объединять следующими.
  70.    Возвращает (i, j, значение).
  71.    """
  72.     best_val = -1.0
  73.     best_i, best_j = None, None
  74.     for idx_i, i in enumerate(labels):
  75.         for idx_j, j in enumerate(labels):
  76.             if idx_j <= idx_i:
  77.                 continue
  78.             v = sim[(i, j)]
  79.             if v > best_val:
  80.                 best_val = v
  81.                 best_i, best_j = i, j
  82.     return best_i, best_j, best_val
  83.  
  84. def merge_clusters(labels, sim, ci, cj):
  85.     """
  86.    Объединяем два кластера ci и cj в один.
  87.    Подобие нового кластера к любому другому считаем по правилу максимума:
  88.    чем ближе хотя бы один из участников — тем ближе весь кластер.
  89.    """
  90.     new_label = f"{ci}, {cj}"
  91.     new_labels = [new_label] + [l for l in labels if l != ci and l != cj]
  92.  
  93.     new_sim = {}
  94.     for i in new_labels:
  95.         for j in new_labels:
  96.             if i == j:
  97.                 new_sim[(i, j)] = 1.0
  98.             elif i == new_label and j == new_label:
  99.                 new_sim[(i, j)] = 1.0
  100.             elif i == new_label:
  101.                 v = max(sim[(ci, j)], sim[(cj, j)])
  102.                 new_sim[(i, j)] = v
  103.                 new_sim[(j, i)] = v
  104.             elif j == new_label:
  105.                 v = max(sim[(i, ci)], sim[(i, cj)])
  106.                 new_sim[(i, j)] = v
  107.                 new_sim[(j, i)] = v
  108.             else:
  109.                 new_sim[(i, j)] = sim[(i, j)]
  110.                 new_sim[(j, i)] = sim[(j, i)]
  111.  
  112.     return new_labels, new_sim
  113.  
  114. # ============================================================
  115. # Основной алгоритм
  116. # ============================================================
  117.  
  118. print("=" * 65)
  119. print("  ИЕРАРХИЧЕСКАЯ АГЛОМЕРАТИВНАЯ КЛАСТЕРИЗАЦИЯ")
  120. print(f"  Порог объединения R = {R}")
  121. print("=" * 65)
  122.  
  123. # Начинаем с каждого объекта в своём кластере
  124. labels = list(labels_init)
  125. sim = build_matrix(labels, raw_sim)
  126.  
  127. print_matrix(labels, sim, "Начальная матрица косинусного подобия")
  128.  
  129. iteration = 0
  130.  
  131. while True:
  132.     ci, cj, val = find_max_pair(labels, sim)
  133.  
  134.     # Если даже самая близкая пара не дотягивает до порога — останавливаемся
  135.     if val < R:
  136.         print(f"\n{'=' * 65}")
  137.         print(f"  Максимальное подобие {val:.6f} < R = {R}")
  138.         print(f"  Дальнейшее объединение невозможно. Алгоритм завершён.")
  139.         break
  140.  
  141.     iteration += 1
  142.     print(f"\n{'=' * 65}")
  143.     print(f"  Итерация {iteration}")
  144.     print(f"  Найдено максимальное подобие: cos({ci}, {cj}) = {val:.6f} >= R = {R}")
  145.     print(f"  Объединяем кластеры: [{ci}] + [{cj}] → [{ci}, {cj}]")
  146.     print(f"  Правило пересчёта: sim(новый, X) = MAX(sim({ci}, X), sim({cj}, X))")
  147.  
  148.     # Сохраняем старую матрицу, чтобы корректно показать исходные значения при пересчёте
  149.     prev_sim = dict(sim)
  150.     labels, sim = merge_clusters(labels, sim, ci, cj)
  151.  
  152.     merged = f"{ci}, {cj}"
  153.     others = [l for l in labels if l != merged]
  154.     if others:
  155.         print(f"\n  Пересчёт подобий для нового кластера [{merged}]:")
  156.         for o in others:
  157.             new_v = sim[(merged, o)]
  158.             v_ci = prev_sim.get((ci, o), prev_sim.get((o, ci), 0.0))
  159.             v_cj = prev_sim.get((cj, o), prev_sim.get((o, cj), 0.0))
  160.             print(f"    sim([{merged}], [{o}]) = MAX(sim([{ci}], [{o}]), sim([{cj}], [{o}]))")
  161.             print(f"                           = MAX({v_ci:.6f}, {v_cj:.6f}) = {new_v:.6f}")
  162.  
  163.     print_matrix(labels, sim, f"Матрица после итерации {iteration}")
  164.  
  165. print(f"\n{'=' * 65}")
  166. print(f"  ИТОГОВЫЕ КЛАСТЕРЫ:")
  167. for i, l in enumerate(labels, 1):
  168.     members = l.replace(" ", "").split(",")
  169.     print(f"  Кластер {i}: {{ {', '.join(members)} }}")
  170. print("=" * 65)
Advertisement
Add Comment
Please, Sign In to add comment