数论(未完成)

一、质数

1.定义:若一个正整数无法被除了1和它自身之外的任何自然数整除,则称该数为质数(或素数),否则称该正整数为合数。

在整个自然数集合中,质数的数量不多,分布比较稀疏,对于一个足够大的整数N,不超过N的质数大约有N/lnN个,即每lnN个数中大约有1个质数。

2.质数的判定:试除法

若一个正整数N为合数,则存在一个能整除N的数T,其中2<=T<=sqrt(n)。
证明:
由定义得,因为N是合数,所以存在一个能整除N的数M,其中2<=M<=N-1。

反证法。假设命题不成立,那么这样的数M一定满足sqrt(n)+1<=M<=N-1.
因为M能整除N,所以它们的商N/M也能整除N。而2<=N/M<=sqrt(n),令T=N/M,这与假设矛盾。
故假设不成立,原命题成立。
证毕。

根据上述命题,我们只需要扫描2~sqrt(n)之间的所有整数,依次检查它们能否整除N,若都不能整除,则N是质数,否则N是合数。
复杂度O(sqrt(n))。

1 bool judge(int n){
2     for(int i=2;i<=sqrt(n);i++){
3         if(n%i==0) return 0;
4     }
5     return 1;
6 }
View Code

其他高效率随机算法:Miller-Robbin

3.质数的筛选

(1)Eratosthenes筛法

Eratosthenes筛法基于这样的想法:任意整数x的倍数2x,3x,…都不是质数。根据质数的定义,上述命题显然成立。

我们可以从2开始,由小到大扫描每个数x,把它的倍数2x,3x,…,[N/x]·x标记为合数。

当扫描到一个数时,若它尚未被标记,则它不能被2~x-1之间的任何数整除,该数就是质数。

1 void Eratosthenes(int n){
2     memset(v,0,sizeof(v));
3     for(int i=2;i<=n;i++){
4         if(v[i]) continue ;
5         cout<<i<<' ';
6         for(int j=1;j<=n/i;j++) v[i*j]=1;
7     }
8 }
View Code

实际上,小于x^2的x的倍数在扫描更小的数时就已经被标记过了。

因此,我们可以对Eratosthenes筛法进行优化,对于每个数x,我们只需要从x^2开始,把x^2,(x+1)*x,[N/x]·x标记为合数即可。

Eratosthenes筛法的时间复杂度为O(NloglogN)。

(2)线性筛法

我们在生成一个需要标记的合数时,每次只向现有的数中乘上一个质因子,并且让它是这个合数的最小质因子。

具体地说,我们采用如下的线性筛法,其中v数组记录每个数的最小质因子。每个合数只会被它的最小质因子筛一次,时间复杂度为O(N)。

 1 int v[N],prime[N],cnt;
 2 void primes(int n){
 3     memset(v,0,sizeof(v));
 4     cnt=0;
 5     for(int i=2;i<=n;i++){
 6         if(!v[i]){
 7             v[i]=i,prime[++cnt]=i;
 8         }
 9         for(int j=1;j<=m;j++){
10             if(prime[j]>v[i]||prime[j]>n/i) break ;
11             v[i*prime[j]]=prime[j];
12         }
13     }
14     for(int i=1;i<=cnt;i++){
15         cout<<prime[i]<<' ';
16     }
17 }
View Code

4.质因数分解

(1)算术基本定理

任何一个大于1的正整数都能唯一分解为有限个质数的乘积,可写作:

N=p1^c1*p2^c2…pm^cm

其中ci都是正整数,pi都是质数,且满足p1<p2<…<pm

(2)试除法

结合质数判定的“试除法”和质数筛选的“Eratosthenes筛法”,我们可以扫描2~sqrt(n)的每个数d,若d能整除N,则从N中除掉所有的因子d,同时累计除去的d的个数。

因为一个合数的因子一定在扫描到这个合数之前就从N中被除掉了,所以在上述过程中能整除N的一定是质数。

最终就得到了质因数分解的结果,易知时间复杂度为O(sqrt(n))。

特别地,若N没有被任何2~sqrt(n)的数整除,则N是质数,无需分解。

 1 void divide(int n){
 2     cnt=0;
 3     for(int i=2;i<=sqrt(n);i++){
 4         if(n%i==0){
 5             prime[++cnt]=i,c[cnt]=0;
 6             while(n%i==0) n/=i,c[cnt]++;
 7         }
 8     }
 9     if(n>1){
10         prime[++cnt]=n,c[cnt]=1;
11     }
12     for(int i=1;i<=cnt;i++){
13         cout<<prine[cnt]<<'^'<<c[i]<<endl;
14     }
15 }
View Code

进阶:“Pollard's Rho”算法

猜你喜欢

转载自www.cnblogs.com/Moxingtianxia/p/11329894.html