Я попробовал это сделать - мне понадобилось несколько попыток, чтобы понять проблему. Я хочу написать то, что я узнал, прежде чем сдаться и перейти к чему-то более простому!
Во-первых, я переделал решение @ shiva, которое дает правильный результат быстрее:
import sys
from functools import lru_cache
def sum_of_digits(number):
summation = 0
while number > 0:
summation += number % 10
number //= 10
return summation
@lru_cache()
def is_prime(number):
if number < 2:
return False
if number % 2 == 0:
return number == 2
divisor = 3
while divisor * divisor <= number:
if number % divisor == 0:
return False
divisor += 2
return True
maximum = int(sys.argv[1])
count = 0
for i in range(maximum + 1):
sum_i = sum_of_digits(i)
for j in range(i, maximum + 1):
if is_prime(sum_i + sum_of_digits(j)):
count += 1
print(count)
Я использую это в качестве эталона ниже по скорости и точности.
Число необходимых простых чисел тривиально, даже для 10 ^ 50, и может / должно быть вычислено заранее. Количество генерируемых цифр также относительно невелико и может быть сохранено / хешировано. Мое решение хэширует все возможные суммы цифр от 0 до 10 ^ N, сохраняя количество раз, когда каждая сумма генерируется как значение. Затем он выполняет пару вложенных циклов над цифровыми суммами (ключами), и если сумма этих сумм является простым числом, он добавляет к количеству произведение количества способов, которыми может быть вычислена каждая сумма (т.е. умножает значения).
import sys
from math import ceil
from collections import defaultdict
VERBOSE = False
def sum_of_digits(number):
summation = 0
while number:
summation += number % 10
number //= 10
return summation
def sieve_primes(n):
sieve = [False, False] + [True] * (n - 1)
divisor = 2
while divisor * divisor <= n:
if sieve[divisor]:
for i in range(divisor * divisor, n + 1, divisor):
sieve[i] = False
divisor += 1
return [number for number in range(2, n + 1) if sieve[number]]
power = int(sys.argv[1]) # testing up to 10 ** power
maximum_sum_of_digits = 18 * power
primes_subset = sieve_primes(maximum_sum_of_digits)
sums_of_digits = defaultdict(int)
for i in range(10 ** power + 1):
sums_of_digits[sum_of_digits(i)] += 1
if VERBOSE:
print('maximum sum of digits:', maximum_sum_of_digits)
print('maximum prime:', primes_subset[-1])
print('number of primes:', len(primes_subset))
print('digit sums cached', len(sums_of_digits))
primes_subset = set(primes_subset)
count = 0
for i in sums_of_digits:
sum_i = sums_of_digits[i]
for j in sums_of_digits:
if i + j in primes_subset:
count += sum_i * sums_of_digits[j]
print(ceil((count + 2) / 2)) # hack to help adjust between duples and no duples count; sigh
(Включите флаг VERBOSE
, чтобы увидеть больше информации о проблеме.)
К сожалению, это учитывает как (X, Y), так и (Y, X), вопреки спецификации проблемы, поэтому в конце кода есть приблизительный хак для исправления, чтобы исправить это. (Пожалуйста, предложите точное исправление!) Я называю свой результат приблизительным, но обычно он недооценивает только на 1 или 2. В отличие от кода @ shiva, этот аргумент принимает степень 10 в качестве аргумента, поскольку его цель - увидеть, насколько близко к 10 ^ 50 можно получить.
Был бы рад увидеть результат для N = 10 ^ 50 (или хотя бы 10 ^ 8) - MBo
@Shiva reworked My Attempt
exact secs approx secs
10^1 24 0.03 24 0.03
10^2 1544 0.04 1544 0.04
10^3 125030 0.49 125029 0.04
10^4 12396120 51.98 12396119 0.05
10^5 1186605815 6223.28 1186605813 0.14
10^6 113305753201 1.15
10^7 11465095351914 12.36
10^8 1120740901676507 137.37
10^9 105887235290733264 1626.87
@ Обновленное решение Шивы бесполезно выше 10 ^ 4, а мои болота ниже 10 ^ 8. Таким образом, достижение 10 ^ 50 займет другой подход. Я надеюсь, что часть этого кода и анализа помогут в этих усилиях.