Вот мои мысли о снижении сложности и времени выполнения.
Вы можете написать сито в O(n log log n)
. Вот разумная реализация:
def sieve(n):
grid = [None for _ in range(n+1)]
i = 2
while i < n+1:
if grid[i] is None:
grid[i] = True
for p in range(2*i, n+1, i):
grid[p] = False
else:
i += 1
return (index for index, b in enumerate(grid) if b)
Есть 6 чисел, и общая сумма, добавленная к первому числу, равна 48. Таким образом, минимально возможное значение для первого числа равно (n - 48) / 6
. В моем сите мы можем итерировать генератор до тех пор, пока число не станет больше.
def get_remaining_sieve(n):
g = sieve(n)
current = next(g)
min_value = (n - 48) / 6
while current < min_value:
current = next(g)
return [current] + list(g)
Теперь просто итерируем по каждому срезу длины 6 и проверяем, соответствует ли разделение требуемому разделению (4, 2, 4, 2, 4).
remaining = get_remaining_sieve(n)
for start in range(len(remaining) - 5):
slice = remaining[start:start+6]
differences = [slice[j] - slice[j-1] for j in range(1, 6)]
if differences == [4, 2, 4, 2, 4]:
print(slice)
Резюме
Основываясь на этих принципах, я пришел к следующему решению:
from itertools import dropwhile, islice
def get_solutions(n):
grid = [None for _ in range(n+1)]
i = 2
while i < n+1:
if grid[i] is None:
grid[i] = True
for p in range(2*i, n+1, i):
grid[p] = False
else:
i += 1
sieve = (index for index, b in enumerate(grid) if b)
min_value = (n - 48) / 6
reduced_sieve = dropwhile(lambda v: v < min_value, sieve)
reference_slice = list(islice(reduced_sieve, 6))
while True:
try:
ref = reference_slice[0]
differences = [v - ref for v in reference_slice[1:]]
if differences == [4, 6, 10, 12, 16]:
yield reference_slice
reference_slice = reference_slice[1:] + [next(reduced_sieve)]
except StopIteration:
break
n = 2000000
print(next(get_solutions(n))) # 695ms
# or for all solutions
for solution in get_solutions(n): # 755ms
print(solution)
Это выполняется в менее чем за секунду на моем компьютере.