1. 试除法判定质数
bool isPrime(int x)
{
if (x<2) return false;
for (int i=2;i<=x/i;i++)
if (x%i==0) return false;
return true;
}
2. 分解质因数
void divide(int x)
{
for (int i=2;i<=x/i;i++) { //找当前的x的大于等于i的质数
if (x%i==0) {//i从2开始,满足条件x%i为0的i一定都是质数,合数肯定不符合条件
int s=0;
while (x%i==0) s++,x/=i; //去除掉x的质因子i,所以x不再包含2~i之间的质因子了
printf("%d %d\n",i,s); //输出i^s
}
}
if (x>1) printf("%d %d\n",x,1);//最后剩下的x要么是个质数,要么是个1
puts("");
}
for (int i=2;i<=x/i;i++)
在这个过程中x是不断变小的,当循环到i时,x就不包含2~i-1之间的质数了,因此我们要找的是当前x的大于等于i的质数。
x针对每次for循环下的变化的x,根据性质n中最多只含有一个大于sqrt(n)的质因子
,如果这个i大于sqrt(x)了,就可以直接退出了,因为我们已经把x的2到i-1,即2到sqrt(x)之间的质数都求出来了,所以**最后剩下的这个x只有两种情况,**要么是那个大于最开始数据输入时的x的sqrt(x)的唯一质数,要么就是其不存在,剩下的x是个1。
在for循环
for (int i=2;i<=x/i;i++)
中,
若x是合数,因为2到i-1之间的质数都不是x的因数了,那i或者大于i的数是他的因数,那么必有x大于i^2,所以循环会继续。
若x是质数,要么直接因不满足
i<=x/i
而退出,要么一直执行空操作,循环到不满足i<=x/i
而退出。
合数x最终会不断变小,变成大于sqrt(x)的质数x或者1。
3. 筛质数
3.1 埃氏筛
朴素做法:
void getPrimes(int n)//求1到n之间的质数
{
for (int i=2;i<=n;i++) {
if(!st[i]) primes[cnt++]=i;
for (int j=i+i;j<=n;j+=i) st[j]=true; //对于每个i,k*i都是合数,置为true (k>=2)
}
}
但是当一个数是合数,n×m时,就不用再把它的倍数置成合数了,因为早在质数n或者质数m时就已经把n×m的倍速的数置成合数了,是n×m的倍数的数是n的倍数的和是m的倍数的数的子集。
埃氏筛:
先初始化,认为所有数都是素数,然后**每找到一个素数x,**把是该素数倍数的合数k*x
都归类为合数。
int primes[N],cnt;//存储质数,cnt是下标
bool st[N];//st[i]为false说明i质数
void getPrimes(int n)//求1到n之间的质数
{
for (int i=2;i<=n;i++)
if(!st[i]) {//获得质数i,并把k*i置成合数
primes[cnt++]=i;
for (int j=i+i;j<=n;j+=i) st[j]=true; //k*i都是合数,置为true (k>=2)
}
}
埃氏筛时间复杂度为O(nlogn),存在一个合数是n×m的情况,在碰到质数n时,在k=m时会把m×n置成合数;当碰到质数m时,在k=n时会把n×m置成合数。二者都是同一个数,即会有重复筛选的情况。
3.2 线性筛
线性筛的核心:每一个合数只会被它的最小质因数筛掉,因此只会被筛一次。
对于枚举到的每个i,假设它的最小质因数是x,在素数表从primes[0]
开始找一直找到x(包括x),在之间的primes[j]
都是小于等于x的。满足性质:
- 若
i%primes[j]==0
,则primes[j]==x
,它就是i的最小质因数,因此primes[j]
必然是primes[j]*i
的最小质因数。 - 若
i%primes[j]!=0
,则primes[j]
小于i的所有质因子,因此,也primes[j]
必然是primes[j]*i
的最小质因数。(因为primes[j]
是从小到大枚举的)
当然,对枚举的primes[j]还要有限制,那就是
primes[j]*i
必须是小于等于n,这样才能st[primes[j]*i]=true;
,防止越界,和判定质数的写法一样,写成primes[j]<=n/i
。primes[j]
的下标j一定要满足条件j<cnt
。
事实上限制2不要也行, 我们用到的最大的质数就是质数i本身,而primes数组已经记录了2到i之间的所有质数。
- 当i为合数时,它的最小质因子必小于i,而我们的primes数组已经记录了2到i-1之间的所有质数;
- 当i为质数时,它的最小质因子就是它本身,我们已经预先把它也放入primes数组了,所以当i为质数时,就是最大遍历的情况,会遍历整个primes数组。
因此,限制2不要也可,但限制1是必须要的,因为primes[j]*i
可能越界。
int primes[N],cnt;
bool st[N];//st[i]为false说明i质数
void getPrimes(int n)//求1到n之间的质数
{
for (int i=2;i<=n;i++) {
if (!st[i]) primes[cnt++]=i;
//找i的最小质因子
for (int j=0;primes[j]<=n/i;j++) {
st[primes[j]*i]=true;
if (i%primes[j]==0) break; //找到了最小质因子,后面就不用再筛了,退出
}
}
}