Модуль быстрой простой факторизации
Я ищу реализация или четкий алгоритм для получения простой факторизации N в любом python, псевдокод или что-нибудь еще хорошо читается. Есть несколько требований / фактов:
- N между 1 и ~20 цифр
- нет предварительно рассчитанной таблицы поиска, хотя memoization в порядке.
- не нужно быть математически доказанным (например, может полагаться на гипотезу Гольдбаха при необходимости)
- не нужно быть точным, допускается быть вероятностным / детерминированным, если это необходимо
Мне нужен быстрый алгоритм простой факторизации, не только для себя, но и для использования во многих других алгоритмах, таких как вычисление Эйлера phi (n).
Я пробовал другие алгоритмы из Википедии и такие, но либо я не мог понять их (ECM), либо я не мог создать рабочую реализацию из алгоритма (Поллард-Брент).
Я действительно заинтересован в алгоритме Полларда-Брента, поэтому любая дополнительная информация/реализация на нем была бы очень приятной.
спасибо!
редактировать
после небольшого беспорядка я создал довольно быстрый модуль prime / factorization. Он сочетает в себе оптимизированный алгоритм пробного разделения, алгоритм Полларда-Брента, тест на примитивность Миллера-Рабина и самый быстрый primesieve, который я нашел в интернете. НОД-это регулярная реализация GCD Евклида (двоичный GCD Евклида много медленнее, чем обычные).
награда
о, радость, щедрость наживное! Но как я могу его выиграть?
- найти оптимизацию или ошибку в моем модуле.
- обеспечить альтернативные/лучшие алгоритмы/реализации.
ответ, который является наиболее полной/конструктивной получает награду.
и, наконец, модуль сам по себе:
import random
def primesbelow(N):
# http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
#""" Input N>=6, Returns a list of primes, 2 <= p < N """
correction = N % 6 > 1
N = {0:N, 1:N-1, 2:N+4, 3:N+3, 4:N+2, 5:N+1}[N%6]
sieve = [True] * (N // 3)
sieve[0] = False
for i in range(int(N ** .5) // 3 + 1):
if sieve[i]:
k = (3 * i + 1) | 1
sieve[k*k // 3::2*k] = [False] * ((N//6 - (k*k)//6 - 1)//k + 1)
sieve[(k*k + 4*k - 2*k*(i%2)) // 3::2*k] = [False] * ((N // 6 - (k*k + 4*k - 2*k*(i%2))//6 - 1) // k + 1)
return [2, 3] + [(3 * i + 1) | 1 for i in range(1, N//3 - correction) if sieve[i]]
smallprimeset = set(primesbelow(100000))
_smallprimeset = 100000
def isprime(n, precision=7):
# http://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Algorithm_and_running_time
if n < 1:
raise ValueError("Out of bounds, first argument must be > 0")
elif n <= 3:
return n >= 2
elif n % 2 == 0:
return False
elif n < _smallprimeset:
return n in smallprimeset
d = n - 1
s = 0
while d % 2 == 0:
d //= 2
s += 1
for repeat in range(precision):
a = random.randrange(2, n - 2)
x = pow(a, d, n)
if x == 1 or x == n - 1: continue
for r in range(s - 1):
x = pow(x, 2, n)
if x == 1: return False
if x == n - 1: break
else: return False
return True
# https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/
def pollard_brent(n):
if n % 2 == 0: return 2
if n % 3 == 0: return 3
y, c, m = random.randint(1, n-1), random.randint(1, n-1), random.randint(1, n-1)
g, r, q = 1, 1, 1
while g == 1:
x = y
for i in range(r):
y = (pow(y, 2, n) + c) % n
k = 0
while k < r and g==1:
ys = y
for i in range(min(m, r-k)):
y = (pow(y, 2, n) + c) % n
q = q * abs(x-y) % n
g = gcd(q, n)
k += m
r *= 2
if g == n:
while True:
ys = (pow(ys, 2, n) + c) % n
g = gcd(abs(x - ys), n)
if g > 1:
break
return g
smallprimes = primesbelow(1000) # might seem low, but 1000*1000 = 1000000, so this will fully factor every composite < 1000000
def primefactors(n, sort=False):
factors = []
for checker in smallprimes:
while n % checker == 0:
factors.append(checker)
n //= checker
if checker > n: break
if n < 2: return factors
while n > 1:
if isprime(n):
factors.append(n)
break
factor = pollard_brent(n) # trial division did not fully factor, switch to pollard-brent
factors.extend(primefactors(factor)) # recurse to factor the not necessarily prime factor returned by pollard-brent
n //= factor
if sort: factors.sort()
return factors
def factorization(n):
factors = {}
for p1 in primefactors(n):
try:
factors[p1] += 1
except KeyError:
factors[p1] = 1
return factors
totients = {}
def totient(n):
if n == 0: return 1
try: return totients[n]
except KeyError: pass
tot = 1
for p, exp in factorization(n).items():
tot *= (p - 1) * p ** (exp - 1)
totients[n] = tot
return tot
def gcd(a, b):
if a == b: return a
while b > 0: a, b = b, a % b
return a
def lcm(a, b):
return abs((a // gcd(a, b)) * b)
8 ответов:
Поллард-Брент реализован в Python:
https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/
если вы не хотите изобретать велосипед, используйте библиотека sympy
pip install sympy
использование функции
sympy.ntheory.factorint
>>> from sympy.ntheory import factorint >>> factorint(10**20+1) {73: 1, 5964848081: 1, 1676321: 1, 137: 1}
вы можете разложить некоторые очень большие числа:
>>> factorint(10**100+1) {401: 1, 5964848081: 1, 1676321: 1, 1601: 1, 1201: 1, 137: 1, 73: 1, 129694419029057750551385771184564274499075700947656757821537291527196801: 1}
нет необходимости расчета
smallprimes
С помощьюprimesbelow
используйтеsmallprimeset
для этого.
smallprimes = (2,) + tuple(n for n in xrange(3,1000,2) if n in smallprimeset)
разделить
primefactors
на две функции для обработкиsmallprimes
иpollard_brent
, это может сэкономить пару итераций, так как все степени smallprimes будут разделены от n.def primefactors(n, sort=False): factors = [] limit = int(n ** .5) + 1 for checker in smallprimes: print smallprimes[-1] if checker > limit: break while n % checker == 0: factors.append(checker) n //= checker if n < 2: return factors else : factors.extend(bigfactors(n,sort)) return factors def bigfactors(n, sort = False): factors = [] while n > 1: if isprime(n): factors.append(n) break factor = pollard_brent(n) factors.extend(bigfactors(factor,sort)) # recurse to factor the not necessarily prime factor returned by pollard-brent n //= factor if sort: factors.sort() return factors
рассматривая проверенные результаты Pomerance, Selfridge и Wagstaff и Jaeschke, вы можете уменьшить повторения в
isprime
который использует Миллер-Рабин тест на первичность. От Wiki.
- если n
- если n
- если n
- если n
- если n
- если n
изменить 1: исправлен обратный звонок
if-else
чтобы добавить bigfactors к факторам вprimefactors
.
даже на текущем, есть несколько пятен, чтобы быть замеченным.
- не
checker*checker
каждый цикл, используйтеs=ceil(sqrt(num))
иchecher < s
- Чечер должен плюс 2 каждый раз, игнорировать все четные числа, кроме 2
- использовать
divmod
вместо%
и//
вы, вероятно, должны сделать некоторые простые обнаружения, которые вы могли бы посмотреть здесь, быстрый алгоритм поиска простых чисел?
вы должны прочитать весь этот блог, хотя, есть несколько алгоритмов, которые он перечисляет для тестирования примитивности.
есть библиотека python с коллекцией тестов на примитивность (включая неправильные для того, что не нужно делать). Это называется pyprimes. Решил, что это стоит упомянуть для целей потомства. Я не думаю, что это включает в себя алгоритмы, которые вы упомянули.
я просто столкнулся с ошибкой в этом коде при факторинге числа
2**1427 * 31
.File "buckets.py", line 48, in prettyprime factors = primefactors.primefactors(n, sort=True) File "/private/tmp/primefactors.py", line 83, in primefactors limit = int(n ** .5) + 1 OverflowError: long int too large to convert to float
этот фрагмент кода:
limit = int(n ** .5) + 1 for checker in smallprimes: if checker > limit: break while n % checker == 0: factors.append(checker) n //= checker limit = int(n ** .5) + 1 if checker > limit: break
следует переписать в
for checker in smallprimes: while n % checker == 0: factors.append(checker) n //= checker if checker > n: break
который, вероятно, будет работать быстрее на реалистичных входах в любом случае. Квадратный корень медленный-в основном эквивалент многих умножений -,
smallprimes
всего несколько десятков членов, и таким образом мы убираем расчетаn ** .5
от плотной внутренней петли, которая определенно полезна когда факторинг чисел, как2**1427
. Там просто нет причин, чтобы вычислитьsqrt(2**1427)
,sqrt(2**1426)
,sqrt(2**1425)
и т. д. так далее., когда все, о чем мы заботимся, это "превышает ли [квадрат] checkern
".переписанный код не вызывает исключений при представлении больших чисел,и это примерно в два раза быстрее по
timeit
(на входах выборки2
и2**718 * 31
).также обратите внимание, что
isprime(2)
возвращает неправильный результат, но это нормально, пока как мы на это не рассчитываем. IMHO вы должны переписать вступление этой функции какif n <= 3: return n >= 2 ...
вы можете факторизовать до предела, а затем использовать brent для получения более высоких коэффициентов
from fractions import gcd from random import randint def brent(N): if N%2==0: return 2 y,c,m = randint(1, N-1),randint(1, N-1),randint(1, N-1) g,r,q = 1,1,1 while g==1: x = y for i in range(r): y = ((y*y)%N+c)%N k = 0 while (k<r and g==1): ys = y for i in range(min(m,r-k)): y = ((y*y)%N+c)%N q = q*(abs(x-y))%N g = gcd(q,N) k = k + m r = r*2 if g==N: while True: ys = ((ys*ys)%N+c)%N g = gcd(abs(x-ys),N) if g>1: break return g def factorize(n1): if n1==0: return [] if n1==1: return [1] n=n1 b=[] p=0 mx=1000000 while n % 2 ==0 : b.append(2);n//=2 while n % 3 ==0 : b.append(3);n//=3 i=5 inc=2 while i <=mx: while n % i ==0 : b.append(i); n//=i i+=inc inc=6-inc while n>mx: p1=n while p1!=p: p=p1 p1=brent(p) b.append(p1);n//=p1 if n!=1:b.append(n) return sorted(b) from functools import reduce #n= 2**1427 * 31 # n= 67898771 * 492574361 * 10000223 *305175781* 722222227*880949 *908909 li=factorize(n) print (li) print (n - reduce(lambda x,y :x*y ,li))