Постоянная Брюна: https://en.wikipedia.org/wiki/Brun%27s_theoremhttp://numbers.computation.free.fr/Constants/Primes/twin.html
Как вычислить константу Бруна до 10 ^ 20 с помощью Python зная, что проверка простоты требует больших затрат, а суммирование результата до 10 ^ 20 - долгая задача
вот моя попытка за 2 цента:
IsPrime: самый быстрый способ проверить, является ли число простым, я знаю digit_root: вычислить цифровой корень числа
Если кто-то знает, что можно улучшить ниже, чтобы достичь вычислений до 10 ^ 20, пожалуйста.
import numpy as np
import math
import time
#Brun's constant
#p B_2(p)
#10^2 1.330990365719...
#10^4 1.616893557432...
#10^6 1.710776930804...
#10^8 1.758815621067...
#10^10 1.787478502719...
#10^12 1.806592419175...
#10^14 1.820244968130...
#10^15 1.825706013240...
#10^16 1.830484424658...
#B_2 should reach 1.9 at p ~ 10^530 which is far beyond any computational project
#B_2*(p)=B_2(p)+ 4C_2/log(p)
#p B2*(p)
#10^2 1.904399633290...
#10^4 1.903598191217...
#10^6 1.901913353327...
#10^8 1.902167937960...
#10^10 1.902160356233...
#10^12 1.902160630437...
#10^14 1.902160577783...
#10^15 1.902160582249...
#10^16 1.902160583104...
def digit_root(number):
return (number - 1) % 9 + 1
first25Primes=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
def IsPrime(n) :
# Corner cases
if (n <= 1) :
return False
if (n <= 3) :
return True
# This is checked so that we can skip
# middle five numbers in below loop
if (n % 2 == 0 or n % 3 == 0) :
return False
#exclude digital root 3 or 6 or 9
if digit_root(n) in (3,6,9):
return False
if (n != 2 and n != 7 and n != 5 and str(n)[len(str(n))-1] not in ("1","3","7","9")): #si le nombre ne se termine pas par 1 3 7 9
return False
for i in first25Primes:
if n%i == 0 and i < n:
return False
if (n>2):
if (not(((n-1) / 4) % 1 == 0 or ((n+1) / 4) % 1 == 0)):
return False
if (n>3):
if (not(((n-1) / 6) % 1 == 0 or ((n+1) / 6) % 1 == 0)):
return False
i = 5
while(i * i <= n) :
if (n % i == 0 or n % (i + 2) == 0) :
return False
i = i + 6
return True
def ComputeB_2Aster(B_2val,p):
return B_2 + (C_2mult4/np.log(p))
start = time.time()
#approx De Brun's
B_2 = np.float64(0)
B_2Aster = np.float64(0)
one = np.float64(1)
#Twin prime constant
C_2 = np.float64(0.6601618158468695739278121100145557784326233602847334133194484233354056423)
C_2mult4 = C_2 * np.float64(4)
lastPrime = 2
lastNonPrime = 1
for p in range(3, 100000000000000000000,2):
if IsPrime(p):
lastNonPrime = p-1
if lastPrime == p-2 and lastNonPrime == p-1:
B_2 = B_2 + (one/np.float64(p)) + (one/np.float64(lastPrime))
lastPrime = p
else:
lastNonPrime = p
if p<10000000000:
if p%1000001==0:
print(f'p:{p} \t\t[elapsed:{time.time()-start}]\nB_2:{B_2:.52f} B_2Aster:{ComputeB_2Aster(B_2,p-2):.52f}\n',end = "")
else:
print(f'p:{p} \t\t[elapsed:{time.time()-start}]\nB_2:{B_2:.52f} B_2Aster:{ComputeB_2Aster(B_2,p-2):.52f}\n',end = "")
print(f'p:{p} \t\t[elapsed:{time.time()-start}]\nB_2:{B_2:.52f} B_2Aster:{ComputeB_2Aster(B_2,p-2):.52f}\n',end = "")
@SimonGoater, какой язык подойдет лучше? С++? Юлия ? Я пробовал использовать numba для использования графического процессора, но это жалко из-за ограничения типов. Попробую с «потоком импорта», хорошая идея! (библиотеки тестирования простоты не очень хорошо оптимизированы для огромных простых чисел:/)
Каково ожидаемое время выполнения для 10⁶ ? Я получаю 0,6808199882507324 с. (в google.colab)
Вы можете эффективно составлять списки простых чисел, используя решето Эратосфена. Вероятно, это было бы быстрее, чем выполнение IsPrimes для каждого целого числа. Вам не нужно проверять четные числа > 2, поскольку они не являются простыми. Есть много способов оптимизировать это, даже до распараллеливания. Я знаю только, как использовать графические процессоры с Opencl c, но заставить графический процессор делать это таким образом было бы довольно большим проектом. Я думаю, вы сможете адаптировать метод просеивания, чтобы более эффективно находить только простые числа-близнецы.
Как заявляет @SimonGoater и указывает, насколько быстро увеличивается время (см. мой ответ ниже), лучше использовать графический процессор и распараллеливать работу.
Теперь, если вы помните свои занятия по теории чисел, есть нечто, называемое решетом Эратосфена, которое представляет собой алгоритм поиска всех простых чисел до любого заданного предела:
algorithm Sieve of Eratosthenes is
input: an integer n > 1.
output: all prime numbers from 2 through n.
let A be an array of Boolean values, indexed by integers 2 to n,
initially all set to true.
for i = 2, 3, 4, ..., not exceeding √n do
if A[i] is true
for j = i2, i2+i, i2+2i, i2+3i, ..., not exceeding n do
set A[j] := false
return all i such that A[i] is true.
Теперь этот алгоритм, переведенный на Python, просто
def sieve_of_eratosthenes(max_num):
is_prime = np.full(max_num + 1, True, dtype=bool)
is_prime[0:2] = False
p = 2
while (p * p <= max_num):
if (is_prime[p] == True):
for i in range(p * p, max_num + 1, p):
is_prime[i] = False
p += 1
return np.nonzero(is_prime)[0]
Теперь, чтобы вычислить константу Бруна, вам понадобится список простых чисел и сумма. Так,
def brun_constant(primes):
sum_primes = mpfr('0')
last_p = mpz('2')
count = 0
for p in primes:
p_gmp = mpz(p)
if count > 0:
if is_prime(p_gmp + 2):
sum_primes += (1/mpfr(p_gmp) + 1/mpfr(p_gmp + 2))
last_p = p_gmp
count += 1
return float(sum_primes)
Итак, ваш полный код будет
import numpy as np
import gmpy2
from gmpy2 import mpz, mpfr, is_prime
import time
def sieve_of_eratosthenes(max_num):
is_prime = np.full(max_num + 1, True, dtype=bool)
is_prime[0:2] = False
p = 2
while (p * p <= max_num):
if (is_prime[p] == True):
for i in range(p * p, max_num + 1, p):
is_prime[i] = False
p += 1
return np.nonzero(is_prime)[0]
def brun_constant(primes):
sum_primes = mpfr('0')
last_p = mpz('2')
count = 0
for p in primes:
p_gmp = mpz(p)
if count > 0:
if is_prime(p_gmp + 2):
sum_primes += (1/mpfr(p_gmp) + 1/mpfr(p_gmp + 2))
last_p = p_gmp
count += 1
return float(sum_primes)
start_time = time.time()
limit = 10**6
primes = sieve_of_eratosthenes(limit)
print(primes)
brun_const = brun_constant(primes)
end_time = time.time()
execution_time = end_time - start_time
print(f"Brun's constant up to {limit}: {brun_const}")
print(execution_time)
Обратите внимание, здесь я выбрал 10^6
в качестве примера:
[ 2 3 5 ... 999961 999979 999983]
Brun's constant up to 1000000: 1.710776930804221
0.4096040725708008
Итак, прошло 0,4 секунды.
За 10^8
я получил
[ 2 3 5 ... 99999959 99999971 99999989]
Brun's constant up to 100000000: 1.758815621067975
47.44146728515625 seconds
Выше я не пробовал, потому что работал в google.colab
и превысил разрешенный лимит.
Обновлять
Как хорошо замечено nocomment
ниже, процесс можно сделать еще быстрее, исключив внутренний цикл в функции sieve_of_erastothenes
, и он будет выглядеть так:
def sieve_of_eratosthenes(max_num):
is_prime = np.full(max_num + 1, True, dtype=bool)
is_prime[0:2] = False
p = 2
while (p * p <= max_num):
if (is_prime[p] == True):
is_prime[p*p :: p] = False
p += 1
return np.nonzero(is_prime)[0]
С этим изменением:
[ 2 3 5 ... 99999959 99999971 99999989]
Brun's constant up to 100000000: 1.7588156210679355
12.536579847335815 seconds
для 10^8
Обновление 2: оценка времени выполнения
Как я уже упоминал выше, я не работаю над машиной, позволяющей мне тестировать 10^20. Итак, я могу дать вам метод оценки времени, используя метод регрессии. Сначала начнём с вычисления времени выполнения для 6,7,8,9,10. Затем аппроксимируйте кривую полиномом:
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
import matplotlib.pyplot as plt
import time
import numpy as np
limits = [10**exp for exp in range(2, 10)]
times = []
for limit in limits:
start_time = time.time()
primes = sieve_of_eratosthenes(limit)
brun_const = brun_constant(primes)
end_time = time.time()
execution_time = end_time - start_time
times.append(execution_time)
print(f"Limit: {limit}, Time: {execution_time}")
X = np.log(limits).reshape(-1, 1)
y = np.log(times).reshape(-1, 1)
poly = PolynomialFeatures(degree=2)
X_poly = poly.fit_transform(X)
model = LinearRegression()
model.fit(X_poly, y)
X_fit = np.linspace(min(X), max(X), 400).reshape(-1, 1)
X_fit_poly = poly.transform(X_fit)
y_fit = model.predict(X_fit_poly)
X_fit = np.exp(X_fit)
y_fit = np.exp(y_fit)
plt.figure(figsize=(10, 6))
plt.scatter(limits, times, color='blue', label='Actual Times')
plt.plot(X_fit, y_fit, color='red', label='Polynomial Model Fit')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Limits')
plt.ylabel('Execution Time (seconds)')
plt.title('Polynomial Regression Fit')
plt.legend()
plt.show()
log_limit_20 = np.log(np.array([10**20], dtype=np.float64)).reshape(-1, 1)
log_limit_20_poly = poly.transform(log_limit_20)
estimated_log_time_20 = model.predict(log_limit_20_poly)
estimated_time_20 = np.exp(estimated_log_time_20)
print(f"Estimated execution time for limit 10^20: {estimated_time_20[0][0]} seconds")
Который дает
Estimated execution time for limit 10^20: 147470457222.1028 seconds
или 40963960.339500725
часов, или 1706493
дней
Итак, я бы предложил использовать машину с графическим процессором.
неправильный подход к увеличению до 10 ^ 20, ОЗУ взорвется ... 10 ^ 16 выделяет 8,88 ПиБ
Python — неправильный подход. Вы просили питонический подход. Я представил питонический подход.
в любом случае спасибо за ваш замечательный вклад
Рад, что это помогло, даже если это не привело к тому, на что вы надеетесь. Хороший вопрос. Надеюсь, таких было больше!
(для такого типа очень тяжелых вычислений, возможно, нам понадобятся распределенные вычисления, такие как prime95 с сайта mersenne.org)
Я не знаю Python, но разве ваше сито не должно начинаться с 2p, а не с p^2?
Вы хотите на 4 порядка превзойти самый известный результат в скрипте Python? Удачи. Если вы серьезно, научитесь программировать графические процессоры и делать это параллельно. Однако я предупреждаю вас: AFAIK не существует готовой библиотеки целочисленных математических вычислений произвольной точности для графических процессоров, не говоря уже о поддержке библиотеки для таких вещей, как тестирование простоты.