快速素数分解模块

orl*_*rlp 69 python algorithm prime-factoring

我正在寻找一种实现清晰的算法,用于在python,伪代码或其他任何可读的内容中获得N的素数因子分解.有一些要求/事实:

  • N介于1到20位之间
  • 没有预先计算的查找表,但是memoization很好.
  • 不需要在数学上证明(例如,如果需要,可以依赖于Goldbach猜想)
  • 不需要精确,如果需要,可以是概率/确定性的

我需要一个快速素数因子分解算法,不仅适用于自身,还适用于许多其他算法,如计算Euler phi(n).

我已经尝试了维基百科的其他算法,但要么我无法理解它们(ECM),要么我无法从算法(Pollard-Brent)创建工作实现.

我对Pollard-Brent算法非常感兴趣,因此对它的任何更多信息/实现都会非常好.

谢谢!

编辑

搞砸了一下后,我创建了一个非常快速的素数/分解模块.它结合了优化的试验分割算法,Pollard-Brent算法,米勒 - 拉宾素性测试和我在互联网上发现的最快的素数.gcd是常规Euclid的GCD实现(二进制Euclid的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)
Run Code Online (Sandbox Code Playgroud)

Col*_*nic 48

如果您不想重新发明轮子,请使用图书馆sympy

pip install sympy
Run Code Online (Sandbox Code Playgroud)

使用该功能 sympy.ntheory.factorint

>>> from sympy.ntheory import factorint
>>> factorint(10**20+1)
{73: 1, 5964848081: 1, 1676321: 1, 137: 1}
Run Code Online (Sandbox Code Playgroud)

你可以考虑一些非常大的数字:

>>> factorint(10**100+1)
{401: 1, 5964848081: 1, 1676321: 1, 1601: 1, 1201: 1, 137: 1, 73: 1, 129694419029057750551385771184564274499075700947656757821537291527196801: 1}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您分享@Colonel Panic。这正是我在寻找的:维护良好的库中的整数分解模块,而不是代码片段。 (2认同)

Roz*_*uur 14

没有必要计算smallprimes使用primesbelow,smallprimeset用于此.

smallprimes = (2,) + tuple(n for n in xrange(3,1000,2) if n in smallprimeset)

将你primefactors分为两个函数进行处理smallprimes和其他for 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
Run Code Online (Sandbox Code Playgroud)

通过考虑Pomerance,Selfridge和Wagstaff以及Jaeschke的验证结果,您可以减少isprime使用Miller-Rabin素性测试的重复次数.来自维基.

  • 如果n <1,373,653,则足以测试a = 2和3;
  • 如果n <9,080,191,则测试a = 31和73就足够了;
  • 如果n <4,759,123,141,则测试a = 2,7和61就足够了;
  • 如果n <2,152,302,898,747,则测试a = 2,3,5,7和11就足够了;
  • 如果n <3,474,749,660,383,则足以测试a = 2,3,5,7,11和13;
  • 如果n <341,550,071,728,321,则测试a = 2,3,5,7,11,13和17就足够了.

编辑1:更正了返回调用if-else以将重要因素附加到因子中primefactors.


Amb*_*ber 12

用Python实现的Pollard-Brent:

https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/

  • 你能否稍微改进这篇文章,这不仅仅是一个链接? (4认同)
  • 好吧,你可以递归地调用它返回的因子,直到它返回"1"为因子. (3认同)
  • 在搞砸了一点之后,在打电话给布伦特之前投入米勒 - 拉宾总理检查是不是很痛苦?因为如果我在11位数的素数上拨打布伦特,它会忙碌1.3秒. (3认同)
  • 这已经搞砸了,对于某些数字,如果您尝试几次,它不会随机分解。例如9。 (3认同)

Kab*_*bie 6

即使在目前的情况下,也有几个地方需要注意。

  1. 不要做checker*checker每一个循环,使用s=ceil(sqrt(num))checher < s
  2. checher 每次都要加 2,忽略除 2 以外的所有偶数
  3. 使用divmod代替%//


mil*_*man 5

你可能应该做一些素数检测,你可以在这里查看, 寻找素数的快速算法?

不过,您应该阅读整个博客,他列出了一些用于测试素性的算法。


Eht*_*ury 5

有一个 python 库,其中包含一系列素性测试(包括不该做的错误测试)。它称为pyprimes。我认为为了后代的目的值得一提。我认为它不包括你提到的算法。