dkl9 commited on 2025-185 22:47:45
Showing 1 changed files, with 67 additions and 0 deletions.
| ... | ... |
@@ -6,6 +6,42 @@ Func: typing.TypeAlias = collections.abc.Callable |
| 6 | 6 |
DistTable: typing.TypeAlias = list[list[float]] |
| 7 | 7 |
IndSeq: typing.TypeAlias = list[int] |
| 8 | 8 |
|
| 9 |
+def tree_weight(x) -> int: |
|
| 10 |
+ if x is None: |
|
| 11 |
+ return 0 |
|
| 12 |
+ elif isinstance(x, BinaryTree): |
|
| 13 |
+ return x.weight |
|
| 14 |
+ return 1 |
|
| 15 |
+ |
|
| 16 |
+class BinaryTree: |
|
| 17 |
+ def __init__(self, a, b=None): |
|
| 18 |
+ self.x = self.y = self.parent = None |
|
| 19 |
+ self.a = a |
|
| 20 |
+ self.b = b |
|
| 21 |
+ self.weight = tree_weight(a) + tree_weight(b) |
|
| 22 |
+ self.usage = 0 |
|
| 23 |
+ |
|
| 24 |
+ def __str__(self) -> str: |
|
| 25 |
+ return str(self.a) if self.b is None else f"({self.a} {self.b})"
|
|
| 26 |
+ |
|
| 27 |
+ def merge(self, other): |
|
| 28 |
+ a, b = self, other |
|
| 29 |
+ while a.parent: |
|
| 30 |
+ a = a.parent |
|
| 31 |
+ while b.parent: |
|
| 32 |
+ b = b.parent |
|
| 33 |
+ if a is b: |
|
| 34 |
+ return a |
|
| 35 |
+ parent = BinaryTree(a, b) |
|
| 36 |
+ a.parent = b.parent = parent |
|
| 37 |
+ return parent |
|
| 38 |
+ |
|
| 39 |
+ def use(self): |
|
| 40 |
+ assert self.usage < self.weight |
|
| 41 |
+ self.usage += 1 |
|
| 42 |
+ if self.parent: |
|
| 43 |
+ self.parent.use() |
|
| 44 |
+ |
|
| 9 | 45 |
def distance_table[T](points: list[T], metric: Func[[T, T], float]) -> DistTable: |
| 10 | 46 |
return [[metric(x, y) for y in points] for x in points] |
| 11 | 47 |
|
| ... | ... |
@@ -26,5 +62,36 @@ def greedy_min_seq(distances: DistTable, score_func: Func[[DistTable, IndSeq], f |
| 26 | 62 |
options.remove(best) |
| 27 | 63 |
return seq |
| 28 | 64 |
|
| 65 |
+def distance_hierarchy(distances: DistTable) -> BinaryTree: |
|
| 66 |
+ n = len(distances) |
|
| 67 |
+ forest = [BinaryTree(i) for i in range(n)] |
|
| 68 |
+ p = None |
|
| 69 |
+ for (i, j) in sorted( |
|
| 70 |
+ ((i, j) for i in range(n) for j in range(n)), |
|
| 71 |
+ key=lambda ij: distances[ij[0]][ij[1]] |
|
| 72 |
+ ): |
|
| 73 |
+ p = forest[i].merge(forest[j]) |
|
| 74 |
+ return p |
|
| 75 |
+ |
|
| 76 |
+def scattered_hierarchy(hierarchy: BinaryTree) -> IndSeq: |
|
| 77 |
+ seq = [] |
|
| 78 |
+ while hierarchy.usage < hierarchy.weight: |
|
| 79 |
+ fb = hierarchy |
|
| 80 |
+ while fb.b is not None: |
|
| 81 |
+ ua = fb.a.usage / fb.a.weight |
|
| 82 |
+ ub = fb.b.usage / fb.b.weight |
|
| 83 |
+ if ua < ub: |
|
| 84 |
+ fb = fb.a |
|
| 85 |
+ elif ua == ub and fb.a.weight > fb.b.weight: |
|
| 86 |
+ fb = fb.a |
|
| 87 |
+ else: |
|
| 88 |
+ fb = fb.b |
|
| 89 |
+ fb.use() |
|
| 90 |
+ seq.append(fb.a) |
|
| 91 |
+ return seq |
|
| 92 |
+ |
|
| 29 | 93 |
dt: DistTable = distance_table([(0, 2), (3, 2), (4, 2), (5, 0), (0, 0)], math.dist) |
| 30 | 94 |
print(greedy_min_seq(dt, score_total)) |
| 95 |
+h: BinaryTree = distance_hierarchy(dt) |
|
| 96 |
+print(h) |
|
| 97 |
+print(scattered_hierarchy(h)) |
|
| 31 | 98 |