最快的方法是将因子分解为10 ^ 18

时间:2018-05-09 10:48:54

标签: algorithm primes prime-factoring sieve-of-eratosthenes factorization

给定一个数字1 <= n <= 10^18,如何在最短的时间复杂度下对其进行分解?

互联网上有很多帖子说明你如何找到素数因素,但在特定情况下,没有一个(至少从我所见过的)中说明他们的好处。

除了Eratosthenes的筛子之外,我使用Pollard的rho算法:

  • 使用筛子,找到前10个 7 数字中的所有素数,然后尽可能将n除以这些素数。
  • 现在使用Pollard的rho算法尝试找到其余的素数,直到n等于1.

我的实施:

#include <iostream>
#include <vector>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <string>

using namespace std;

typedef unsigned long long ull;
typedef long double ld;
typedef pair <ull, int> pui;
#define x first
#define y second
#define mp make_pair

bool prime[10000005];
vector <ull> p;

void initprime(){
    prime[2] = 1;
    for(int i = 3 ; i < 10000005 ; i += 2){
        prime[i] = 1;
    }
    for(int i = 3 ; i * i < 10000005 ; i += 2){
        if(prime[i]){
            for(int j = i * i ; j < 10000005 ; j += 2 * i){
                prime[j] = 0;
            }
        }
    }
    for(int i = 0 ; i < 10000005 ; ++i){
        if(prime[i]){
            p.push_back((ull)i);
        }
    }
}

ull modularpow(ull base, ull exp, ull mod){
    ull ret = 1;
    while(exp){
        if(exp & 1){
            ret = (ret * base) % mod;
        }
        exp >>= 1;
        base = (base * base) % mod;
    }
    return ret;
}

ull gcd(ull x, ull y){
    while(y){
        ull temp = y;
        y = x % y;
        x = temp;
    }
    return x;
}

ull pollardrho(ull n){
    srand(time(NULL));
    if(n == 1)
        return n;
    ull x = (rand() % (n - 2)) + 2;
    ull y = x;
    ull c = (rand() % (n - 1)) + 1;
    ull d = 1;
    while(d == 1){
        x = (modularpow(x, 2, n) + c + n) % n;
        y = (modularpow(y, 2, n) + c + n) % n;
        y = (modularpow(y, 2, n) + c + n) % n;
        d = gcd(abs(x - y), n);
        if(d == n){
            return pollardrho(n);
        }
    }
    return d;
}
int main ()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
initprime();
ull n;
cin >> n;
ull c = n;
vector <pui> o;
for(vector <ull>::iterator i = p.begin() ; i != p.end() ; ++i){
    ull t = *i;
    if(!(n % t)){
        o.push_back(mp(t, 0));
    }
    while(!(n % t)){
        n /= t;
        o[o.size() - 1].y++;
    }
}
while(n > 1){
    ull u = pollardrho(n);
    o.push_back(mp(u, 0));
    while(!(n % u)){
        n /= u;
        o[o.size() - 1].y++;
    }
    if(n < 10000005){
        if(prime[n]){
            o.push_back(mp(n, 1));
        }
    }
}
return 0;
}

有没有更快的方法来计算这些数字?如果可能,请解释原因以及源代码。

2 个答案:

答案 0 :(得分:4)

方法

假设您有一个数字n,最多可达10 18 并且您想要对它进行推理。因为这个数字可以小到1并且大到10 18 ,所以它可以是素数和复合数,这将是我的方法 -

  1. 使用miller rabin primality testing,确保该数字是复合数。
  2. 使用素数最多10 6 的因子n,可以使用sieve of Eratosthenes计算。
  3. 现在n的更新值是这样的,它具有仅超过10 6 的素数因子,因为n的值仍然可以大到10 18 ,我们得出结论,这个数字要么是素数,要么只有两个素数因子(不一定是不同的)。

    1. 再次运行Miller Rabin以确保号码不是素数。
    2. 使用Pollard rho algorithm获得一个素数因素。
    3. 你现在已经完全分解了。

      让我们看一下上述方法的时间复杂性:

      • 米勒·拉宾接受O(log n)
      • Eratosthenes的筛子需要O(n*log n)
      • 我分享的Pollard rho的实施需要O(n^0.25)

      时间复杂度

      步骤2占用的最大时间等于O(10^7),这又是上述算法的复杂性。这意味着您可以在几秒钟内找到几乎所有编程语言的因子分解。

      空间复杂度

      空间仅在实施筛子的步骤2中使用,并且等于O(10^6)。再次,非常实用。

      实施

      C++14中实施了

      Complete Code。代码有一个隐藏的bug。您可以在下一部分中显示它,或跳过挑战;)

      代码中的错误

        

      line 105中,迭代到i<=np。否则,您可能会错过prime[np]=999983是主要因素

      的情况

      挑战

      给我一​​个n的值,如果有的话,共享代码会导致错误的素数因子化。

      加成

      n有多少这样的值?

      暗示
        

      对于n的这样的值,Line 119中的断言可能会失败。

        

      让我们来电话P=999983n = p*q*r形式的所有数字,其中p,q,r是素数&gt; = P,使得它们中的至少一个等于P将导致错误的素因数分解。

      奖金解决方案
        

      正好四个这样的数字:{P 0 3 ,P 0 2 < / sup> P 1 ,P 0 2 P 2 ,P 0 P 1 2 },其中P 0 = P = 999983,P 1 = next_prime(P 0 )= 1000003,P 2 = next_prime(P 1 )= 1000033。

答案 1 :(得分:2)

现代处理器上64位输入的最快解决方案是少量试验(数量会有所不同,但100以下是常见的),其次是Pollard的Rho。您将需要使用Miller-Rabin或BPSW进行良好的确定性素性测试,以及处理多种因素的基础设施(例如,如果复合材料分成更多复合材料)。对于32位,您可以进一步优化这些内容。

你需要一个快速的mulmod,因为它是Pollard的Rho,Miller-Rabin和Lucas测试的核心。理想情况下,这是作为一个小的汇编程序片段完成的。

时间应小于1毫秒,以计算任何64位输入。在50位以下显着更快。

如Ben Buhrow的spBrent实现所示,算法P2&#39;&#39;来自布伦特原油的1980年论文似乎与我所知道的其他实施一样快。它使用布伦特改进的循环发现以及通过必要的附加回溯来延迟GCD的有用技巧。

请参阅this thread on Mersenneforum了解各种解决方案的一些混乱细节和基准测试。我有不同大小的这些和其他实现的许多基准,但没有发布任何东西(部分原因是有很多方法可以查看数据)。

其中一个非常有趣的事情是SQUFOF,多年来被认为是64位高端产品的更好解决方案,不再具有竞争力。 SQUFOF的优势在于只需要一个快速的完美方形探测器来获得最佳速度,而且不必非常快速。