| """ |
| Minimal Collatz subtree construction up to N, using the structural |
| branch algorithm from the paper. |
| |
| This file implements: |
| |
| - Algorithm 5: GenerateCollatzBranches |
| - Algorithm 6: BuildCollatzSubtreeEdges |
| |
| The combined effect is to construct the minimal Collatz subtree |
| (containing all natural numbers 1..N, plus necessary ancestors) |
| as a directed graph with edges (child -> parent) following the |
| forward Collatz map: |
| |
| T(n) = n/2 if n is even |
| 3n + 1 if n is odd |
| |
| Edges are of the form (n, T(n)), including the structural edge (1, 4). |
| """ |
|
|
| from __future__ import annotations |
|
|
| from typing import Dict, List, Tuple |
|
|
| import pandas as pd |
|
|
|
|
| |
| |
| |
|
|
| def generate_collatz_branches(N: int) -> Dict[int, List[int]]: |
| """ |
| Implement Algorithm 5: GenerateCollatzBranches. |
| |
| Parameters |
| ---------- |
| N : int |
| Natural number N (upper bound for starting odd numbers). |
| |
| Returns |
| ------- |
| Dict[int, List[int]] |
| Dictionary `Tree` mapping each odd root y to an increasing list |
| of values of the form y * 2^m. Initially, each odd x ≤ N is mapped |
| to its doubling sequence up to N, then extended as needed when |
| exploring Collatz branches from all odd x in [3, N]. |
| |
| Notes |
| ----- |
| Structural summary: |
| |
| 1) For each odd x ≤ N, create the base branch: |
| Tree[x] = [x, 2x, 4x, ..., 2^k x] (all ≤ N). |
| |
| 2) For each odd x from 3 to N, repeatedly perform: |
| b = 3x + 1 |
| m = b & (-b) # largest power of 2 dividing b |
| y = b // m # odd part of b |
| |
| and extend the branch for y so that it contains b (and any |
| intermediate doublings) if needed. Then set x <- y and repeat |
| until x = 1 or we break due to an already-covered b. |
| """ |
|
|
| if N < 1: |
| raise ValueError("N must be a positive integer.") |
|
|
| Tree: Dict[int, List[int]] = {} |
|
|
| |
| |
| |
| for x in range(1, N + 1, 2): |
| seq: List[int] = [] |
| power = 1 |
| while x * power <= N: |
| seq.append(x * power) |
| power *= 2 |
| Tree[x] = seq |
|
|
| |
| |
| |
| for start_x in range(3, N + 1, 2): |
| x = start_x |
|
|
| |
| while x != 1: |
| |
| b = 3 * x + 1 |
|
|
| |
| m = b & -b |
|
|
| |
| y = b // m |
|
|
| |
| if y in Tree: |
| |
| if b not in Tree[y]: |
| |
| power = 1 << len(Tree[y]) |
|
|
| |
| while y * power <= b: |
| |
| Tree[y].append(y * power) |
| |
| power *= 2 |
| else: |
| |
| break |
| else: |
| |
| |
| power = 1 |
| seq: List[int] = [] |
|
|
| |
| while y * power <= b: |
| |
| seq.append(y * power) |
| |
| power *= 2 |
|
|
| |
| Tree[y] = seq |
|
|
| |
| x = y |
|
|
| |
| return Tree |
|
|
|
|
| |
| |
| |
|
|
| def build_collatz_subtree_edges(N: int, Tree: Dict[int, List[int]]) -> pd.DataFrame: |
| """ |
| Implement Algorithm 6: BuildCollatzSubtreeEdges. |
| |
| Parameters |
| ---------- |
| N : int |
| Upper bound N (not explicitly used inside, but kept for interface |
| consistency with the paper). |
| Tree : Dict[int, List[int]] |
| Dictionary produced by generate_collatz_branches(N). |
| |
| Returns |
| ------- |
| pd.DataFrame |
| DataFrame with columns ["Source", "Target"], encoding directed |
| edges (child -> parent) of a connected minimal Collatz subtree |
| containing all integers up to N. |
| |
| The pseudocode is followed line by line: |
| |
| 1) For each branch list S in Tree, add edges S[i] -> S[i-1]. |
| 2) For each odd root x > 1, compute: |
| b = 3x + 1 |
| m = b & (-b) |
| y = b / m |
| find the index t of b in Tree[y], and add edge: |
| Tree[x][0] -> Tree[y][t] |
| which corresponds structurally to x -> 3x + 1. |
| 3) Finally, add the structural edge (1, 4). |
| """ |
|
|
| edges: List[Tuple[int, int]] = [] |
|
|
| |
| |
| |
| for _, S in Tree.items(): |
| |
| for i in range(1, len(S)): |
| child = S[i] |
| parent = S[i - 1] |
| edges.append((child, parent)) |
|
|
| |
| |
| |
| for x in sorted(Tree.keys()): |
| if x > 1: |
| |
| b = 3 * x + 1 |
|
|
| |
| m = b & -b |
|
|
| |
| y = b // m |
|
|
| |
| if y not in Tree: |
| |
| continue |
|
|
| try: |
| t = Tree[y].index(b) |
| except ValueError: |
| |
| continue |
|
|
| |
| root_x = Tree[x][0] |
| match_y = Tree[y][t] |
| edges.append((root_x, match_y)) |
|
|
| |
| |
| |
| edges.append((1, 4)) |
|
|
| |
| |
| |
| df = pd.DataFrame(edges, columns=["Source", "Target"]).drop_duplicates() |
| return df |
|
|
|
|
| |
| |
| |
|
|
| def generate_minimal_collatz_subtree_edges(N: int) -> pd.DataFrame: |
| """ |
| High-level helper that runs Algorithm 5 and Algorithm 6: |
| |
| Tree = GenerateCollatzBranches(N) |
| edges = BuildCollatzSubtreeEdges(N, Tree) |
| |
| and returns the resulting edge DataFrame. |
| |
| Parameters |
| ---------- |
| N : int |
| Upper bound on the natural numbers to be included (1..N). |
| |
| Returns |
| ------- |
| pd.DataFrame |
| Edge list with columns ["Source", "Target"]. |
| """ |
| Tree = generate_collatz_branches(N) |
| df_edges = build_collatz_subtree_edges(N, Tree) |
| return df_edges |