相信大家都知道这个威尔逊定理定理吧,定理内容就是对于一个素数p, (p−1)!≡−1(modp) ,
这个东西的证明可以看我前面写的文章。
费马小定理:对于一个素数p,且a不整除p,那么
ap−1≡1(modp) ,但是这定理的逆定理却不成立。
而威尔逊定理是判断一个数是否是素数的充分必要条件。
http://blog.csdn.net/ganjingxian/article/details/76268706,这里就不再赘述。利用这个东西我们可以推出另一个定理。
对于一个素数n,方程 x2≡1(modp) 模p的根有两个,一个是n-1,另一个是1。我们把这两个根称为以n为模的平凡平方根。
结论:如果一个数存在1的非平凡平方根,则n是合数。
①选取多个基数进行测试,(也就是说找多几个数进行测试)。
②寻找模n为1的非平凡平方根,做法如下:
先令 n−1=2u×t ,其中 u≥1,t为奇数 ,
an−1=a2u×t=(at)2u
先算出 at ,当然是用快速幂,然后再平方u次,如果在平方的过程中发现当前这个 (at)2k 模n等于1,那么n就不是素数。
③算完之后,看看算出来的这个值是不是1,若不是则这个数不是素数。
举个例子,
n=341,a=2,n−1=340=22×85 , x=285≡32(mod341) , 322≡1(mod341) ,那么341肯定不是质数。
这个测试的正确率并不是百分之百的,经过研究表明,它测试s次的出错概率最多为 2−s ,但是一般来说这个算法的正确性很高,时间复杂度也不高。
使用pascal的同志,对不起,我写的程序太烂了,测试23333333333,就215了。
var
s,n:int64;
function mull(a,b,n:int64):int64;
var
tmp:int64;
begin
int64(tmp):=int64(double(a*b/n+1e-6))*n;
exit(a*b-tmp);
end;
function f(a,b,n:int64):int64;
var
t,y:int64;
begin
t:=1;
y:=a;
while b<>0 do
begin
if(b and 1)=1 then
t:=t*y mod n;
y:=y*y mod n;
b:=b shr 1;
end;
exit(t);
end;
function judge(n,a:int64):boolean;
var
u,t,x,next,i:int64;
begin
u:=0;
t:=n-1;
while t mod 2=0 do
begin
inc(u);
t:=t div 2;
end;
x:=f(a,t,n);
i:=1;
while i<=u do
begin
next:=x*x mod n;
if ((next=1)and(x<>1)and(x<>n-1)) then
begin
exit(true);
end;
x:=next;
inc(i);
end;
if x<>1 then exit(true);
exit(false);
end;
function miller(n,s:int64):boolean;
var
i:longint;
a:int64;
begin
if n=2 then
exit(true);
if (n=1) or (n mod 2=0) then exit(false);
i:=1;
while i<=s do
begin
a:=random(n-2)+2;
if judge(n,a) then
exit(false);
inc(i);
end;
exit(true);
end;
begin
randomize;
readln(n,s);
if miller(n,s) then
writeln('yes')
else
writeln('no');
end.
c++
#include
#include
#include
#define fo (i,a,b) for (int i=(a);i<=(b);i++)
#define fd (i,b,a) for (int i=(b);i>=(a);i--);
typedef long long ll;
typedef long double db;
ll n,s;
ll mult(ll a,ll b,ll n)
{
ll temp=((ll)((db)a*b/n+1e-6)*n);
return a*b-temp;
}
ll pow(ll a,ll b,ll n)
{
ll t=1,y=a;
while (b)
{
if (b&1)
t=mult(t,y,n);
y=mult(y,t,n);
b=b>>1;
}
return t;
}
bool judge(ll n,ll a)
{
ll u=0,t=n-1;
while(t%2==0){u++;t=t/2;}
ll x=pow(a,t,n);
for(int i=1;i<=u;i++)
{
ll next=mult(x,x,n);
if ((next==1)&&(x!=1)&&(x!=n-1))
return true;
x=next;
}
if (x!=1)return true;
return false;
}
bool Miller_Rabin(ll n,int s)
{
if (n==2)return true;
if (n<2 || n % 2==0)return false;
for(int i=1;i<=s;i++)
{
ll a=rand()%(n-2)+2;
if (judge(n,a))return false;
}
return true;
}
int main()
{
srand(time(0));
scanf("%lld%d",&n,&s);
if (Miller_Rabin(n,s))
printf("yes");
else
printf("no");
return 0;
}
c++就不怕会像pascal一样215。我觉得应该是我写的代码太丑了。
上面的代码中有一个黑科技,就是模的时候将它转化一下,变成mult(pascal中为mull),原理: a(modb)=a−[ab]×b ,这个东西的证明十分简单,证法如下:
设a=bk+r。
由于我的水平有限,难免会有些写错的地方,希望大家批评指正,多多包容,thank you for your patience.