质数

质数筛:


 

暴力筛
代码:

1 bool check_prime(int n)
2 {
3   if(n==1) return 0;
4   for(int i=1;i<=n;i++)
5   if(n%i==0) return 1;
6   return 0;
7 }

 

六筛法
代码:

 1 bool check_prime(int n)
 2 {
 3     if(n==1) return 0;//1既不是质数也不是合数 
 4     if(n==2||n==3) return 1;//2,3不是6两侧的数但他是质数 
 5     if(n%6!=1&&n%6!=5) return 0;//质数只能是6两侧 
 6     int x=sqrt(n);//筛到根号n 
 7     for(int i=5;i<=x;i++)
 8         if(n/i==0||n/(i+2)==0) return 0;//如果他还有约数 
 9     return 1;//若没有约数 
10 }

 

引理:算术基本定理——任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积
所以只需要n无法被质数整除n即为质数
由于6两侧的数不是2和3的倍数所以不需要用2和3来筛
也不需要用6n+2,3,4来筛因为这些都是2和3的倍数,如果该数能被筛掉则改该数也能被2和3整除,不符合上述条件

线性筛 (也叫欧拉筛)
代码:

int primes[N], cnt; // primes[]存储所有素数
bool st[N]; // st[x]存储x是否被筛掉
void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i]) primes[cnt ++ ] = i;//如果没有没有被筛掉就存入primes 
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}            

引理:算术基本定理——任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积
显然,在所有大于0的自然数中,除了质数就是合数.要求质数,只需要”筛”去所有的合数即可.如何筛去合数
这里就应用到了算术基本定理.关于合数,这里我们需要注意两点:
(1).所有合数都要筛到
(2).不能有重复筛选,否则无法达到在线性时间内的运行了
要做到第一点,根据算术基本定理:n=q*m(q表示最小质数,m=N/q,显然m>=q),对N内的m和q进行枚举
这样我们就保证了能枚举N以内的所有整数,然而我们还不能保证枚举的合数不重复。

先来思考一下为什么会有重复,由于对于任意一个n=m*q,m和q是相对的两个状态,m大了q就变小,m小了q就变大。设n=m1*q1=m2*q2,q1<=q2<=m2<=m1我们这里需要保证当且仅当在q1是最小质因数时能求解,也就是去除q2和m2的情况.因为q1与q2互质,且q1<q2,则有m2%q1=0;由于每一个i值都是不同的因此我们认为当前的i值符合m1的条件因此临界条件为q1=q2,m2=m1,此时q1再增大时就有危险因为此时不符合除去q2和m2的条件。

①算法特性决定了找到的素数只能从自身开始乘。比如找到素数5时,它的2、3、4倍已经删不到了,这样避免了重复删。(虽然单独列出但也可以合并进②)

②if(i%p[j]==0)break;语句实现不重复删。证明很简单,自然数i从最小素数2开始乘,i×p[j]以每个p[j]为最小质因数,直到p[j]整除i时,i×p[j]还是以p[j]为最小质因数,但下一个数i×p[j+1]=(i/p[j])×p[j]×p[j+1]中p[j+1]就不是最小质因数了,所以不必继续乘了。

 

所以关键步骤来了: if(i%peimes[j]==0) break;
当i是primes[j]的倍数时直接跳出循环,设i=primes[j]*t,此时有i*primes[j+1]=primes[j]*t*primes[j+1]
可以看到此时的最小质数时primes[j],为了避免与当m=primes[j+1]*t时有重复,这里可以通过break跳过计算
此外还需注意:要保证m从2开始,因为1*q=1且i%1==0;


质因子:


试除法求质因子

 1 void divide(int x)
 2 {
 3     for (int i = 2; i <= x / i; i ++ )//只用筛到根号x 
 4         if (x % i == 0)
 5         {
 6             int s = 0;
 7             while (x % i == 0) x /= i, s ++ ;
 8             printf("%d %d\n",i,s);
 9         }
10     if (x > 1) printf("%d 1\n",x);
11     cout << endl;
12 }

分解质因数用短除法,把一个数进行短除可以分解成若干个质数相乘

分解质因数要从最小的质数2开始除,直到没有因数2再除以下一个质数3…直至除得的商也是质数为止。

posted @ 2020-10-20 19:01  筱翼深凉  阅读(248)  评论(0编辑  收藏  举报