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