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

C 关于如何使我的算法更快的建议,c,performance,algorithm,primes,C,Performance,Algorithm,Primes,这是我在C语言中针对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; i

这是我在C语言中针对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,这显然是错误的。 有人能帮忙吗?
非常感谢。

有几件事需要考虑:

正如@Alex Reynolds所指出的,您试图计算的数字可能太大,无法用整数表示。您可能需要使用long或uint64\t来存储数字。单凭这一点就可以解决问题

与其检查每个除数并查看哪些除数是素数,不如尝试以下方法:将n设置为600851475143。对于从2开始的每个整数,尝试将n除以该整数。如果它干净地除掉,那么从n中除掉该数字的所有副本,并将最大的素数因子记录为当前整数。如果你想一想,你会注意到,你会考虑的唯一因子是质数。作为一个有用的提示-如果n没有除1以外的除数小于√n、 那就是黄金时期。这可能有助于为您的搜索空间提供一个上界,该上界比您正在使用的除以2的技巧要紧密得多

与其将除数增加1,不如尝试将2作为除数进行测试,然后只除以奇数3、5、7、9、11等。除2之外,没有偶数是素数,因此这将使需要除以的数字减半

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


希望这有帮助

需要考虑的几件事:

正如@Alex Reynolds所指出的,您试图计算的数字可能太大,无法用整数表示。您可能需要使用long或uint64\t来存储数字。单凭这一点就可以解决问题

与其检查每个除数并查看哪些除数是素数,不如尝试以下方法:将n设置为600851475143。对于从2开始的每个整数,尝试将n除以该整数。如果它干净地除掉,那么从n中除掉该数字的所有副本,并将最大的素数因子记录为当前整数。如果你想一想,你会注意到,你会考虑的唯一因子是质数。作为一个有用的提示-如果n没有除1以外的除数小于√n、 那就是黄金时期。这可能有助于为您的搜索空间提供一个上界,该上界比您正在使用的除以2的技巧要紧密得多

与其将除数增加1,不如尝试将2作为除数进行测试,然后只除以奇数3、5、7、9、11等。除2之外,没有偶数是素数,因此这将使需要除以的数字减半

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


希望这有帮助

我建议您改进代码的素性检查部分。方法的运行时间为On2,因此应该使用更有效的算法,如著名的Miller–Rabin素性测试和Oklog3n。 我在这里为您提供了一个伪代码,您可以自己编写代码:

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
我提供了一个示例,让您可以看到python中的一个实现,该实现还将此算法与您的算法进行比较。顺便说一句,这个算法在网络上有很多实现,但我认为自己纠正它可以帮助您更好地理解它。

我建议您改进代码中的素性检查部分。方法的运行时间为On2,因此应该使用更有效的算法,如著名的Miller–Rabin素性测试和Oklog3n。 我在这里为您提供了一个伪代码,您可以自己编写代码:

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
我提供了一个示例,让您可以看到python中的一个实现,该实现还将此算法与您的算法进行比较。顺便说一句,这个算法在网络上有很多实现,但我认为自己纠正它可以帮助您更好地理解它。

尝试以下代码。它基本上实现了公认答案中的要点。唯一的改进是,它使用轮分解跳过了2、3和5的所有倍数


可以做的另一件事是预先计算所有小于2^32的素数,而不是下载它们,然后只除以素数。我所知道的最快的方法是。这是一个使用OpenMP的版本,它可以在不到一秒钟的时间内找到多达10亿个素数,请尝试以下代码。它基本上实现了公认答案中的要点。唯一的改进是,它使用轮分解跳过了2、3和5的所有倍数

阿诺特

可以做的事情是预先计算所有小于2^32的素数,而不是下载它们,然后只除以素数。我所知道的最快的方法是。这里是一个使用OpenMP的版本,它在不到1秒的时间内找到素数为10亿的/< P>你可能不知道,但是这实际上是C++。这可以取决于你的平台以及它如何定义int,但是查找INTXMAX,看看为什么你可能会遇到600851475143的问题作为你的测试值。特别是,请更仔细地查看scanf中的格式说明符。使用所有和额外的警告-Wall-Wextra进行编译,看看是否会收到关于溢出的警告;我将从阅读它们开始。你能做的最显著的改进就是只测试数字的sqrt。从sqrtn开始,而不是从input/2开始。@EricLippert此问题的最快解决方案不涉及生成素数。例如,你可能不知道,但这实际上是C++。这可以取决于你的平台以及它如何定义int,但是查找INTXMAX,看看为什么你可能会遇到600851475143的问题作为测试值。特别是,请更仔细地查看scanf中的格式说明符。使用所有和额外的警告-Wall-Wextra进行编译,看看是否会收到关于溢出的警告;我将从阅读它们开始。你能做的最显著的改进就是只测试数字的sqrt。从sqrtn开始,而不是从input/2开始。@EricLippert此问题的最快解决方案不涉及生成素数。参考例如…+1,一般来说是好的建议,但这是不正确的:没有一个数的素数因子大于它的平方根。例如,22有素数因子2和11,11明显大于√22不过,你只需要检查除数,直到√n因为任何大于√n需要乘以另一个小于√n、 三,。可以通过排除3的倍数进行改进。除了2和3外,每个素数的形式都是p=6*k-1或p=6*k+1。@LutzL,也可以通过排除5的倍数来进一步改进。这就是所谓的轮分解。在我的回答中,我排除了2,3和5的倍数。但是额外排除5和25 mod 30是否值得为使用更紧凑的forint p=5,d=2付出额外的努力;p*p@LutzL,p*p不是因为溢出而危险吗?通常最好使用sqrtN.+1作为好的建议,但这是错误的:没有一个数的素数因子大于其平方根。例如,22有素数因子2和11,11明显大于√22不过,你只需要检查除数,直到√n因为任何大于√n需要乘以另一个小于√n、 三,。可以通过排除3的倍数进行改进。除了2和3外,每个素数的形式都是p=6*k-1或p=6*k+1。@LutzL,也可以通过排除5的倍数来进一步改进。这就是所谓的轮分解。在我的回答中,我排除了2,3和5的倍数。但是额外排除5和25 mod 30是否值得为使用更紧凑的forint p=5,d=2付出额外的努力;p*p@LutzL,由于溢出,p*p不是很危险吗?最好使用sqrtN。此代码查找小于x的最大因子,但要求它查找最大的素数因子。对于x=12,结果将是2,6,但6不是一个基本因子。所以划分出小的素数因子是最快的,这也解释了它们的多重性。@LutzL,我修正了它。现在它返回的最大素数是6857,这是正确的答案,应该可以了。小巧美观。只有因子2、3、5(如果存在)未被去除。因为测试编号没有它们,所以这无关紧要,但请尝试使用x=600。whilex>2&&x%2==0x/=2,whilex>3&&x%3==0x/=3;whilex>5&&x%5==0 x/=5@卢兹尔,再次感谢你!我添加了您的更改。现在它适用于所有x。需要13.5秒才能确定2^63-25是3.6 GHz的质数沙桥。此代码找到的最大因子小于x,但要求它提供最大的质数因子。对于x=12,结果将是2,6,但6不是一个基本因子。所以划分出小的素数因子是最快的,这也解释了它们的多重性。@LutzL,我修正了它。现在它返回的最大素数是6857,这是正确的答案,应该可以了。小巧美观。只有因子2、3、5(如果存在)未被去除。因为测试编号没有它们,所以这无关紧要,但请尝试使用x=600。whilex>2&&x%2==0x/=2,whilex>3&&x%3==0x/=3;whilex>5&&x%5==0 x/=5@卢兹尔,再次感谢你!我添加了您的更改。现在它适用于所有x。需要13.5秒才能确定2^63-25是3.6 GHz的主要沙桥。