利用费马小定理进行素性测试
Description
给出一个整数N,请利用费马小定理(Fermat’s Little Theorem)测试该数是否素数。
Input
多测试用例。每个测试用一行:一个正整数N ( 3 ≤ N ≤ 9223372036854775807 ,N的范围就是 long long 的范围。注意:本OJ不支持__int64这种类型,所以,如果要用 __int64类型,可直接把它改为 long long类型。__int64的输入输出用 %I64d ,long long的输入输出用%lld )
Output
每个测试用例输出一行结果:如果N是素数,输出yes,否则,输出no 。
Sample Input
127
5
67
68
Sample Output
yes
yes
yes
no
一开始的思路,只用了费马小定理的代码,递归次数太多了,如果数太大需要耗费很长时间才能计算出结果。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//费马小定理
long long fermat(long long a,long long n,long long m)
{
//递归出口
if(m==0)return 1;
else if(m==1)
return a;
//递归
else{
if(m%2==0)
return (fermat(a,n,m/2)%n*fermat(a,n,m/2)%n)%n;
else
return ((fermat(a,n,m/2)%n*fermat(a,n,m/2)%n)%n*a)%n;
}
}
int main(void)
{
long long n,a=0;
scanf("%lld",&n);
while(n>=3&&n<=9223372036854775807)
{
srand((unsigned)time(NULL));
//生成一个足够大的随机数(64位)
for(int i=0;i<4;i++)
{
a=a+rand();
a=a<<16;
}
//求n的模
a=a%n;
if(a<2)a=2;
if(fermat(a,n,n-1)==1)
printf("yes\n");
else
printf("no\n");
scanf("%lld",&n);
}
return 0;
}
对于这道题,如果单纯只用费马小定理的话来做这道算法的话呢,理论是可以的,但是时间复杂度太高,计算一个大的数时需要很长时间,所以出现了Time Limit Exceeded。所以不得不再去研究如何去设计一种可以快速得出结果的算法。(研究了好几天还是没有头绪,最后还是去请教了一位已经AC过这道题的师兄-.-)
师兄给出了做这道题的思路,对于这道题他使用一种高效又准确的算法,通过整合费马小定理+高精度算法+高精度求余+快速幂这几个算法来实现。首先我们先来了解一下什么是费马小定理、高精度算法、高精度求余和快速幂,这几个算法也挺容易理解的。
- 费马小定理
费马小定理(Fermat’s little theorem)是数论中的一个重要定理,在1636年提出,其内容为: 假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p),即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
注意:可以用费马小定理判断素数,但有一定概率是错误的。费马小定理只是素数的必要条件,不是充分条件,即:满足费马小定理的数不一定是素数。
虽然有一定的概率出错,但效率高,而且也可以通过使用随机数生成a 或者增加验证费马小定理的次数来减小出错率。
- 高精度乘法
计算机内部直接使用int和double等数据类型储存数字是有范围限制的,如:C语言的int类型与开发环境平台有关,可能是16位(2^16),也可能是32位(2^32)。当数据运算大小超过范围后,计算机