关于如何使我的算法更快的建议

时间:2014-03-03 17:38:36

标签: c performance algorithm primes

这是我在C中的代码,来自project-Euler的问题#3,我必须找到最大的素数因子600851475143。

#include <stdio.h>
#include <stdlib.h>

bool is_prime(long int number){
     long int j;
     for (j=2; j<=number/2; j++){
             if (number%j==0) return false;
             if (j==number/2) return true;
            }
    }

int main(){

     long int input;
     scanf("%d", &input);
     long int factor;
     int ans=0;

     for (factor=input/2; factor>1; factor--){
             if (input%factor==0 && is_prime(factor)) {
                     ans = factor;
                     break;
                    }
            }

     printf("%d\n", ans);
     system("pause");
     return 0;
    }

虽然它适用于小数字,但逐渐需要花费越来越多的时间来给出答案。最后,对于600851475143,代码返回0,这显然是错误的。 有人可以帮忙吗? 非常感谢。

3 个答案:

答案 0 :(得分:3)

需要考虑的一些事项:

  1. 正如@Alex Reynolds指出的那样,你想要考虑的数字可能太大而无法容纳int。您可能需要使用longuint64_t来存储该号码。仅这一点就可以解决问题。

  2. 不是检查每个除数并查看哪些是素数,而是想要尝试这种方法:将n设置为600851475143。对于从2向上的每个整数,尝试将n除以该整数。如果它干净地分开,则从n中除去该数字的所有副本,并将最大素数因子记录为当前整数。如果你仔细想一想,你会发现你用这种方式考虑的唯一除数是素数。作为一个有用的提示 - 如果n没有小于√n的除数(除了1),那么它就是素数。这可能会让你的搜索空间上限比你正在使用的两个技巧更加紧密。

  3. 不是将除数增加1,而是尝试将2作为除数测试,然后仅除以奇数(3,5,7,9,11等)。除2之外的偶数不是素数,所以这会将你需要除以的数字减半。

  4. 或者,通过从互联网下载素数列表创建一个存储所有素数达√600851475143的文件,然后测试每个素数以查看是否有任何素数除以600851475143并取最大值。 : - )

    希望这有帮助!

答案 1 :(得分:2)

我建议你改进代码的primality check部分。你的方法的运行时间是 O(n 2 所以你应该使用一个更有效的算法,就像众所周知的Miller-Rabin素性测试 O(KLOG 3 n)的 的。 我在这里为您提供伪代码,您可以自己编写代码:

Input: n > 3, an odd integer to be tested for primality;
Input: k, a parameter that determines the accuracy of the test
Output: composite if n is composite, otherwise probably prime
write n − 1 as 2s·d with d odd by factoring powers of 2 from n − 1
WitnessLoop: repeat k times:
   pick a random integer a in the range [2, n − 2]
   x ← ad mod n
   if x = 1 or x = n − 1 then do next WitnessLoop
   repeat s − 1 times:
      x ← x2 mod n
      if x = 1 then return composite
      if x = n − 1 then do next WitnessLoop
   return composite
return probably prime

我提供link给你看python中的一个实现,它也将你的算法与你的算法进行比较。顺便说一句,这个算法在网络上有很多实现,但我认为自己做对可以帮助你更好地理解它。

答案 2 :(得分:2)

尝试以下代码。它基本上实现了接受答案中的要点。唯一的改进是它使用车轮分解http://en.wikipedia.org/wiki/Wheel_factorization

跳过2,3和5的所有倍数
//find largest prime factor for x <2^64
#include <stdio.h>
#include <stdint.h>
int main() {
    uint64_t x = 600851475143;
    int wheel[] = {4,2,4,2,4,6,2,6};
    while(x>2 && x%2==0) x/=2;
    while(x>3 && x%3==0) x/=3;
    while(x>5 && x%5==0) x/=5;   
    for(uint64_t j=0, i=7; i<=x/i; i+=wheel[j++], j%=8) {
        while(x>i && x%i==0) x/=i;
    }
    printf("%llu\n", x);
}

可以做的另一件事是预先计算小于2 ^ 32的所有素数(而不是下载它们),然后只划分素数。我知道这样做的最快方法是Sieve of Eratosthenes。这是一个使用OpenMP的版本,它可以在不到一秒的时间内找到高达10亿的素数http://create.stephan-brumme.com/eratosthenes/