基本定义: 在大于1的自然数中,除了1和它本身以外不再有其他因数。
注意:1既不是素数也不是合数;2是最小的素数,也是唯一的偶素数。素数的个数是无限的.
任何一个大于1的正整数都能被唯一分解为有限个质数的乘积,可写作:
N=a1c1 a2c2a3c3……amcm
其中ci都是正整数,ai都是素数且满足a1 (此定理不多解释,只能意会,不能言传。) 如果p为素数,则 ( p -1 )! ≡ -1 ( mod p ) 其中首行为是不是质数。然后挨个输出 其次,a,2a,3a……,(p-1)a中没有任何两个同余与mod p的。 于是:a,2a,3a……(p-1)a 对mod p的同余既不为0(缘于首先),也没有两个同余相同(缘于其次),因此,这p-1个数对mod p的同余一定是1,2,3,……(p-1)的某一种排列(因为,同余不为零也不相同,但在mod p的情况下,结果只有0~p-1) 即:a × 2a × 3a……×(p-1)a mod p≡1×2×3×……×(p-1)(mod p) 在上述条件的情况下,这必成立~~(这不多说)~~ 然后我们进行化简 我们根据威尔逊定理(上一个定理),因为p是素数,所以(p-1)!和p互质(因为威尔逊定理(p-1)!+1是p的倍数,不妨设(p-1)!+1=mp,则(p-1)!=mp-1,mp-1和p互质)。所以我们可以直接约去(p-1)!。 然后我们便证明了伟大的费马小定理. 如果一个数N为非素数,则存在一个能整除N的数K,其中2<=K<=sqrt(N) 证明: 我们利用反证法,假设K不在此范围内(2<=K<=sqrt(N)) 因为它是一个合数,所以在(2~N-1)内肯定会有一个数是它的约数(我们假设为M),又因为这个数不在此范围内(2<=这个数<=sqrt(N)),所以M属于[sqrt(N)+1,N-1]。不妨令 评价:代码简单,容易理解,比较好写加好像,判断单个数貌似也不慢(好像只有优点的样子) 但要判断N个数的话,恐怕就要JJ了。 这个方法其实就是利用了定理三,费马小定理的逆定理,由于费马小定理的逆定理不一定成立,所以我们要多次测试。 方法的可行性: 若N通过一次测试,则N不是素数的概率为(1、4) (求1~N之内的素数) 评价:这个方法不建议用,即使N范围比较小。 原因一:时间复杂度高,O(N t log2n) 原因二:容错率提高了N倍 评价: 代码: 基本思想:质数的倍数一定不是素数。 方法:用一个v数组标记此数是不是合数(0表示素数,1表示非素数)。先假设所有的数都是素数(初始化v数组为零),从小到大枚举每一个素数,把x的倍数都标记为非素数(标记为一)。枚举到一个数x,若它尚未被标记,则他不能被2~x-1之间的任意数整除,该数就是素数 注意:在枚举时我们要从二开始枚举。一的情况要特判 代码: 评价:代码简单友好写,速度也很快,埃氏筛法很常用。但个别卡这种筛法的题就没有办法了。 疑问:为什么 j循环要从(i~N/i) 上界 :因为 i的i倍的之前的数已经被其余的标记了,不需要重复标记。例如:3的2倍标记的数,已经被2的3倍给标记了。所以我们只需要标记3的(3~n/3)倍就好。 时间复杂度接近 O(n),为O(nloglogn)。 是一种常用的筛法。 为什么不是O(n)? 因为此筛法仍然会重复标记合数,例如12既可以被2标记,也可以被3表记。因为12=2×6,12=3×4。其根本原因是算法不能唯一确定12的产生方式。 因此,线性筛法应运而生。 方法:我们在生成一个需要标记的合数时,每次只向现有的数乘上一个质因子(缘于原理一,唯一分解定理),并且让它是这个和数的最小质因子。(避免重复标记) 这相当于让合数的质因子从大到小累积,即让12只以 3×2×2这一种方式产生(为什么不是2×2×3,因为在统计质因子时,先是2后是3,所以6是因为3×2得来,然后枚举到6时,又×2,所以是3×2×2。 代码 顾名思义,时间复杂度O(n) 算法定理:唯一分解定理 评价:算法速度快, 素数基本上就这点基础知识,其他的会延伸,或者把一些算法结合起来。(那你还好意思称详解)二:威尔逊定理
威尔逊定理的逆定理也成立,即如果 ( p -1 )! ≡ -1 ( mod p ),则 p为素数。
(p-1)!+1一定是p的倍数。
(此定理知道就行,在初等数论中,由于阶乘爆炸性增长,所以意义不大。貌似此定理不大出现)
证明的话,蒟蒻不会。但检验还是可以的
检验: scanf("%d",&p);
int ans=1;
for(register int i=1;i<=p-1;i++){//计算p-1的阶乘
ans=(ans*i)%p;
}
printf("%d\n",(ans+p)%p);
printf("%d",(p-1)%p);
三.费马小定理
(这个定理貌似不陌生)
若p为素数,a为正整数,且a和p互质(牢记使用条件),则a^(p-1)≡1(mod p)
(讲真的,此定理我不想证明;但毕竟不能完全背离详解的题目吧。)
证明:
首先,p-1个整数,a,2a,3a,……,(p-1)a中没有一个是p的倍数,因为p为素数,a为正整数,且a和p互质。(我觉得这很显然。。。但我不会说为么。。。。)
即:ap-1× (p-1)!≡(p-1)! (mod p)(感觉身体被掏空。。。。。。)三.素数的判定
法一:试除法
(这可能是高中数学的专用名词) K=N/M ,则肯定在此范围内了(2<=K<=sqrt(N))。
方法:我们只需扫描一下此范围内的数有没有N的约数,如果有则不是素数,反之就是素数。不难知道:时间复杂度为O(sqrt(N))
代码: inline bool judge(int n){
if(n==1) return false;//特判1
for(register int i=2;i<=sqrt(n);i++){//扫描的过程
if(n%i==0) return false;
}
return true;
}
法二:Miller-Rabin素性测试
( 不要问我为什么,因为蒟蒻不懂 ) 若N通过t次测试,则N不是素数的概率为(1/4t),所以测试次数越多越准确。事实上t为5时,N不是素数的概率已经为(1/128),高达99.99%。(如果判断错,原因有二:一:你脸黑,二:故意卡)
方法的失误:假如随机选取四个数为2,3,5,7 ,则在2.5*1013 以内唯一一个判断失误的数为3215031751
评价:小心脸黑,注意被卡! ,时间复杂度(O(tlog2(n))),貌似也不慢的样子。
代码:#include
三.素数的筛法
法一:暴力判断1(N范围内,每个都用Miller-Rabin素性测试)
(但还是很小很小,也不算是原因吧。)代码就不放了法二:暴力判断2(N范围内,每个数都试除法判断)
这个方法若在N范围比较小的情况下,闲的没事写写玩玩吧。inline bool judge(int n){
if(n==1) return false;
for(register int i=2;i<=sqrt(n);i++){
if(n%i==0) return false;
}
return true;
}
//以下主函数里面
for(register int i=1;i<=cnt;i++){
if(judge(i)) printf("YES\n");//判断cnt范围内的i是不是质数
else printf("NO\n");
}
法三:Eratosthenes 筛选法(埃氏筛法)
(这是定义啊)void primes(int n){
memset(v,0,sizeof(v));//初始化v数组
v[1]=1;//特判1
for(int i=2;i<=n;i++){
if(v[i]==0)
for(int j=i;j<=n/i;j++) v[i*j]=1;//标记倍数
}
return;
}
下界:范围不超过n法四:线性筛法
(这个地方不太好懂,主要是我描述的可能不太清楚,还是看代码吧) ) inline void primes(int n){
for(register int i=2;i<=n;i++){
if(!v[i]){
v[i]=i;//素数的最小质因子是其本身
prime[++m]=i;//记录素数
}
for(register int j=1;j<=m;j++){
if(prime[j]>n/i||prime[j]>v[i]) break;//超出范围n或有比prime[j]更小的质因子直接终止(prime[]单调递增)
v[i*prime[j]]=prime[j];标记最小质因子
}
}
for(int i=1;i<=m;i++)
printf("%d ",prime[i]);//输出素数
}
每个数只被标记一次,所以是O(n)O(n)能不快吗 ,一般不会被卡吧,其实埃氏筛法我也没见到被卡的时候。