# import numpy as np # def heuristic(n, rng=None): # if rng is None or isinstance(rng, int): # rng = np.random.default_rng(rng) # S = set() # candidates = list(range(1, n+1)) # # Prioritize numbers with fewer representations as part of an AP # # Here, we simply use the count of APs they can form, but more sophisticated methods exist # candidates.sort(key=lambda x: sum(1 for y in S if 2*y-x in S or 2*x-y in S or y+x in [2*z for z in S])) # for x in candidates: # if not any((2*x - y in S and y in S) or (2*y - x in S and y in S) for y in S): # S.add(x) # return S import numpy as np def count_3ap_memberships(N: int) -> np.ndarray: """ counts[x] = number of 3-APs (a,b,c) with a<b<c in [1..N] where x is one of {a,b,c}. Time: O(N^2) via iterating (middle b, step d). """ counts = np.zeros(N + 1, dtype=np.int64) # 1-indexed for b in range(1, N + 1): max_d = min(b - 1, N - b) for d in range(1, max_d + 1): a = b - d c = b + d counts[a] += 1 counts[b] += 1 counts[c] += 1 return counts def would_create_3ap(S: set[int], x: int) -> bool: """ Check whether adding x to S would create any 3-term AP entirely in S ∪ {x}. Equivalent: exists y in S such that (2*y - x) in S OR (2*x - y) in S. """ for y in S: if (2 * y - x) in S: return True # (x, y, 2y-x) with y middle if (2 * x - y) in S: return True # (y, x, 2x-y) with x middle return False def salem_spencer_greedy(N: int, rng=None, tie_break="random", verbose=False): """ Greedy Salem-Spencer construction on [1..N]: - Precompute global 3-AP membership counts for each integer as sorting key. - Iterate candidates in ascending key order; add x if it does not create a 3-AP. tie_break: - "random": shuffle within same-key bucket using rng - "asc": smaller x first within same-key bucket - "desc": larger x first within same-key bucket """ if rng is None or isinstance(rng, int): rng = np.random.default_rng(rng) counts = count_3ap_memberships(N) candidates = list(range(1, N + 1)) # Tie-breaking strategy if tie_break == "random": rng.shuffle(candidates) candidates.sort(key=lambda x: counts[x]) # stable sort keeps shuffled order within ties elif tie_break == "asc": candidates.sort(key=lambda x: (counts[x], x)) elif tie_break == "desc": candidates.sort(key=lambda x: (counts[x], -x)) else: raise ValueError("tie_break must be one of: 'random', 'asc', 'desc'") if verbose: print("candidate -> key(count_3ap_memberships)") for x in candidates: print(f"{x:>4} -> {counts[x]}") S: set[int] = set() for x in candidates: if not would_create_3ap(S, x): S.add(x) return S, counts, candidates # ----------------------- # Example usage # ----------------------- if __name__ == "__main__": N = 82 S, counts, order = salem_spencer_greedy(N, rng=0, tie_break="asc", verbose=False) print("N =", N) print("|S| =", len(S)) print("S (sorted) =", sorted(S)) # show first 15 candidates with their keys print("First 15 candidates (x, key):", [(x, int(counts[x])) for x in order]) # X = heuristic(1094) # print(X, len(X))
Standard input is empty
N = 82 |S| = 20 S (sorted) = [1, 2, 4, 5, 10, 11, 13, 14, 28, 29, 31, 32, 69, 70, 72, 73, 78, 79, 81, 82] First 15 candidates (x, key): [(1, 40), (82, 40), (2, 41), (81, 41), (3, 42), (80, 42), (4, 43), (79, 43), (5, 44), (78, 44), (6, 45), (77, 45), (7, 46), (76, 46), (8, 47), (75, 47), (9, 48), (74, 48), (10, 49), (73, 49), (11, 50), (72, 50), (12, 51), (71, 51), (13, 52), (70, 52), (14, 53), (69, 53), (15, 54), (68, 54), (16, 55), (67, 55), (17, 56), (66, 56), (18, 57), (65, 57), (19, 58), (64, 58), (20, 59), (63, 59), (21, 60), (62, 60), (22, 61), (61, 61), (23, 62), (60, 62), (24, 63), (59, 63), (25, 64), (58, 64), (26, 65), (57, 65), (27, 66), (56, 66), (28, 67), (55, 67), (29, 68), (54, 68), (30, 69), (53, 69), (31, 70), (52, 70), (32, 71), (51, 71), (33, 72), (50, 72), (34, 73), (49, 73), (35, 74), (48, 74), (36, 75), (47, 75), (37, 76), (46, 76), (38, 77), (45, 77), (39, 78), (44, 78), (40, 79), (43, 79), (41, 80), (42, 80)]