素性检测
如何判断一个数是素数还是合数?
最简单的方法
从 挨个除 n,如果能有一个能整除 n,则n不是素数,负责n是素数
缺点 如果n特别大,那么这种方法就非常耗时间且不容易实现
1 利用费马小定理判断
如果 p是素数 则 , 任意从1 - p 取a 的值进行判断,如果不成立,则a是合数,否则p可能是素数
缺点 卡米歇尔数
卡米歇尔数是这样的合数, 即对于每个整数1 <= a <= n, 都有
卡米歇尔数的考塞特判别法
设 n 是合数, 则n 是卡米歇尔数当且仅当n是奇数,且整数n的每个整数p满足下列两个条件:
(1) :
(2 ) :
2 合数的拉宾-米勒测试
设n是奇数,记n-1 = 2^k * q, q 是奇数, 对不被n整除的某个a, 如果下述两个条件都成立, 则n是合数。
(a )
( b )
这个定理的准确性建立在
如果n是奇合数,则1 与 n-1 之间至少有75% 的数可作为n的拉宾 - 米勒证据
意思就是如果n是奇合数, 则1 到n-1 之间至少有75% 的数使得拉宾-米勒测试成立
这样如果随机选取100个值,其中如果如果没有一个使得拉宾-米勒测试成立,则n为合数的概率小于0.25^100, 它近似等于
模板
// 能够判断的范围,1<=x < 2^63;
#include<bits/stdc++.h>
using namespace std;
//typedef long long LL;
const int LEN = 1e6+1;
bool vis[LEN];
//int prime[LEN];
int Prime[LEN];
int cnt = 1;
typedef unsigned long long LL;
LL modular_multi(LL x,LL y,LL mo) {
LL t;
x%=mo;
for(t=0;y;x=(x<<1)%mo,y>>=1)
if (y&1)
t=(t+x)%mo;
return t;
}
LL modular_exp(LL num,LL t,LL mo) {
LL ret=1,temp=num%mo;
for(;t;t>>=1,temp=modular_multi(temp,temp,mo))
if (t&1)
ret=modular_multi(ret,temp,mo);
return ret;
}
bool miller_rabin(LL n) {
if (n==2||n==7||n==61)
return true;
if (n==1||(n&1)==0)
return false;
int t=0,num[3]={2,7,61};//2,7,61对unsigned int内的所有数够用了,最小不能判断的数为4 759 123 141;用2,3,7,61在 10^16 内唯一不能判断的数是 46 856 248 225 981
LL a,x,y,u=n-1;
while((u&1)==0)
t++,u>>=1;
for(int i=0;i<3;i++) {
a=num[i];
x=modular_exp(a,u,n);
for(int j=0;j<t;j++) {
y=modular_multi(x,x,n);
if (y==1&&x!=1&&x!=n-1)
return false;
//其中用到定理,如果对模n存在1的非平凡平方根,则n是合数。
//如果一个数x满足方程x^2≡1 (mod n),但x不等于对模n来说1的两个‘平凡’平方根:1或-1,则x是对模n来说1的非平凡平方根
x=y;
}
if (x!=1)//根据费马小定理,若n是素数,有a^(n-1)≡1(mod n).因此n不可能是素数
return false;
}
return true;
}
void init(void)
{
int n = LEN -1;
for(int i = 2; i <= n; ++i)
{
if(!vis[i])
{
Prime[cnt++] = i;
for(LL j = (LL)i * i; j <= n; j += i)
vis[j] = 1;
}
}
}
bool isPrime(LL n)
{
if(n < 1e6)
{
for(LL i = 1;i < cnt&&Prime[i] < n; ++i)
{
if(n % Prime[i] == 0)
return false;
}
return true;
}
else
return miller_rabin(n);
}