Каков наилучший подход к вычислению константы Бруна с помощью Python (ведьма включает тест на простоту и долгосрочную сумму)?

Постоянная Брюна: 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 = "")

Вы хотите на 4 порядка превзойти самый известный результат в скрипте Python? Удачи. Если вы серьезно, научитесь программировать графические процессоры и делать это параллельно. Однако я предупреждаю вас: AFAIK не существует готовой библиотеки целочисленных математических вычислений произвольной точности для графических процессоров, не говоря уже о поддержке библиотеки для таких вещей, как тестирование простоты.

Simon Goater 17.04.2024 11:08

@SimonGoater, какой язык подойдет лучше? С++? Юлия ? Я пробовал использовать numba для использования графического процессора, но это жалко из-за ограничения типов. Попробую с «потоком импорта», хорошая идея! (библиотеки тестирования простоты не очень хорошо оптимизированы для огромных простых чисел:/)

Ch'nycos 17.04.2024 11:30

Каково ожидаемое время выполнения для 10⁶ ? Я получаю 0,6808199882507324 с. (в google.colab)

Serge de Gosson de Varennes 17.04.2024 12:30

Вы можете эффективно составлять списки простых чисел, используя решето Эратосфена. Вероятно, это было бы быстрее, чем выполнение IsPrimes для каждого целого числа. Вам не нужно проверять четные числа > 2, поскольку они не являются простыми. Есть много способов оптимизировать это, даже до распараллеливания. Я знаю только, как использовать графические процессоры с Opencl c, но заставить графический процессор делать это таким образом было бы довольно большим проектом. Я думаю, вы сможете адаптировать метод просеивания, чтобы более эффективно находить только простые числа-близнецы.

Simon Goater 17.04.2024 12:43

Как заявляет @SimonGoater и указывает, насколько быстро увеличивается время (см. мой ответ ниже), лучше использовать графический процессор и распараллеливать работу.

Serge de Gosson de Varennes 17.04.2024 14:02
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
5
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Теперь, если вы помните свои занятия по теории чисел, есть нечто, называемое решетом Эратосфена, которое представляет собой алгоритм поиска всех простых чисел до любого заданного предела:

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 ПиБ

Ch'nycos 17.04.2024 14:32

Python — неправильный подход. Вы просили питонический подход. Я представил питонический подход.

Serge de Gosson de Varennes 17.04.2024 14:34

в любом случае спасибо за ваш замечательный вклад

Ch'nycos 17.04.2024 14:52

Рад, что это помогло, даже если это не привело к тому, на что вы надеетесь. Хороший вопрос. Надеюсь, таких было больше!

Serge de Gosson de Varennes 17.04.2024 14:54

(для такого типа очень тяжелых вычислений, возможно, нам понадобятся распределенные вычисления, такие как prime95 с сайта mersenne.org)

Ch'nycos 17.04.2024 15:18

Я не знаю Python, но разве ваше сито не должно начинаться с 2p, а не с p^2?

Simon Goater 17.04.2024 16:47

Другие вопросы по теме