如何加快这种素性测试

时间:2017-01-07 06:37:49

标签: c++ primes

我想找到给定数字的最大素数因子。经过多次尝试,我已经增强了测试以应对相当大的数字(即以毫秒为单位)。现在的问题是,如果超过10亿,执行时间将永远存在,可以这么说。我想知道我是否可以做更多改进并缩短执行时间。我希望有更好的执行时间,因为在这个链接Prime Factors Calculator中,执行时间非常快。我此时的目标号码是600851475143.代码相当不言自明。 注意:我已经考虑了Eratosthenes算法的Sieve,但没有关于执行时间的运气。

#include <iostream>
#include <cmath>

bool isPrime(int n)
{
    if (n==2)
        return true;

    if (n%2==0)
        return false;

    for (int i(3);i<=sqrt(n);i+=2) // ignore even numbers and go up to sqrt(n)
        if (n%i==0)
            return false;

    return true;
}

int main()
{
    int max(0);
    long long target(600851475143);

    if( target%2 == 0 )
        max = 2;

    for ( int i(3); i<target; i+=2 ){ // loop through odd numbers. 
        if( target%i == 0 )  // check for common factor
            if( isPrime(i) ) // check for prime common factor
                max = i;
    }

    std::cout << "The greatest prime common factor is " << max << "\n";


    return 0;
}

5 个答案:

答案 0 :(得分:3)

我可以看到一个明显的优化:

for (int i(3);i<=sqrt(n);i+=2) // ignore even numbers and go up to sqrt(n)
每次你可以将结果缓存到变量中时,

而不是计算sqrt

auto maxFactor = static_cast<int>sqrt(n);
for (int i(3); i <= maxFactor; i+=2);

我认为这可能导致加速的原因是sqrt处理floating point arithematic并且编译器通常并不慷慨地优化浮点数。 gcc有一个特殊的标志ffast-math,可以明确地启用浮点优化。

对于您提到的目标范围以外的数字,您需要更好的算法。 repeated divisioning应该足够了。

以下代码(http://ideone.com/RoAmHd)几乎没有时间完成:

int main() {
    long long input = 600851475143;
    long long mx = 0;
    for (int x = 2; x <= input/x; ++x){
        while(input%x==0) {input/=x; mx = x; }

    }
    if (input > 1){
        mx = input;
    }
    cout << mx << endl;
    return 0;
}

重复划分背后的想法是,如果数字已经是 p 的因子,它也是 p ^ 2,p ^ 3,p ^ 4的因子.... 即可。因此,我们继续消除因素,因此只有最终因素仍然存在才能最终划分数字。

答案 1 :(得分:1)

你不需要进行素性测试。试试这个算法:

function factors(n)
    f := 2
    while f * f <= n
        if n % f == 0
            output f
            n := n / f
        else
            f := f + 1
    output n

您不需要素性测试,因为每个步骤的试验因子增加1,因此任何复合试验因子都已经由其较小的组成素数处理。

我将留给您使用适当的数据类型在C ++中实现。这不是计算整数的最快方法,但对于Project Euler 3来说已经足够了。

答案 2 :(得分:0)

请注意,除2和3外,所有素数都与6的倍数相邻。

以下代码通过以下方式减少了迭代总数:

  • 利用上述事实
  • 每次找到新的素因子时减少target
#include <iostream>


bool CheckFactor(long long& target,long long factor)
{
    if (target%factor == 0)
    {
        do target /= factor;
        while (target%factor == 0);
        return true;
    }
    return false;
}


long long GetMaxFactor(long long target)
{
    long long maxFactor = 1;

    if (CheckFactor(target,2))
        maxFactor = 2;

    if (CheckFactor(target,3))
        maxFactor = 3;

    // Check only factors that are adjacent to multiples of 6
    for (long long factor = 5, add = 2; factor*factor <= target; factor += add, add = 6-add)
    {
        if (CheckFactor(target,factor))
            maxFactor = factor;
    }

    if (target > 1)
        return target;
    return maxFactor;
}


int main()
{
    long long target = 600851475143;
    std::cout << "The greatest prime factor of " << target << " is " << GetMaxFactor(target) << std::endl;
    return 0;
}

答案 3 :(得分:0)

for ( int i(3); i<target; i+=2 ){ // loop through odd numbers. 
    if( target%i == 0 )  // check for common factor
        if( isPrime(i) ) // check for prime common factor
            max = i;

这是此代码的前两行,而不是素数检查,几乎占用了所有时间。您将目标划分为从3target-1的所有数字。这需要大约target/2个分区。

此外,targetlong long,而i仅为int。尺寸太小可能会导致无限循环。

最后,此代码不计算最大的主要公因子。它计算目标的最大质数除数,并且效率非常低。那么你真正需要什么?

任何事情都是一个坏主意&#34; max&#34;在c ++中,因为max是一个标准函数。

答案 4 :(得分:0)

这是我的基本版本:

int main() {
    long long input = 600851475143L;

    long long pMax = 0;

    // Deal with prime 2.
    while (input % 2 == 0) {
        input /= 2;
        pMax = 2;
    }

    // Deal with odd primes.
    for (long long x = 3; x * x <= input; x += 2) {
        while (input % x == 0) { 
            input /= x;
            pMax = x;
        }
    }

    // Check for unfactorised input - must be prime.
    if (input > 1) {
        pMax = input;
    }

    std::cout << "The greatest prime common factor is " << pMax << "\n";

    return 0;
}

通过使用Newton-Raphson整数平方根方法为循环设置(大部分)固定限制,可以进一步加快速度。如果可用,则需要重写主循环。

    long long limit = iSqrt(input)
    for (long long x = 3; x <= limit; x += 2) {
        if (input % x == 0) {
            pMax = x;
            do {
                input /= x;
            } while (input % x == 0);
            limit = iSqrt(input); // Value of input changed so reset limit.
        }
    }

只有在找到新因子并且input的值发生变化时,才会计算平方根。