判断质数
对于一个数n,不借助后面质数数组的情况下,我们可以从2往后遍历来判断n是否有因子,从而判断其是否为质数。也就是从小到大找第一个质因子。时间复杂度O()。
cpp
bool is_prime(int n)
{
for(int i = 2; i <= n / i; i ++)
if(n % i == 0)return false;
return true;
}
筛质数
目标:获得质数数组、O(1)判断一个数是不是质数
扩展:获得一个数的最小质因子
所需数组:
cpp
int primes[N], cnt; // 质数数组
bool st[N]; // 判断质数
int p[N]; // 最小质因子(可选)
普通筛,用遍历到的数的倍数筛掉合数,时间复杂度O(nloglog n)
cpp
void sieve(int n)
{
for(int i = 2; i <= n; i ++)
{
if(!st[i])primes[cnt ++] = i;
for(int j = i * 2; j <= n; j += i)
st[j] = true;
}
}
可以发现,在遍历的i时候明显重复给某些合数赋值了,比如12会在i=2,i=3,i=4,i=6的时候反复被筛掉。
埃式筛,用质数的倍数去筛掉合数,时间复杂度O(nloglog n)
cpp
void sieve(int n)
{
for(int i = 2; i <= n; i ++)
{
if(!st[i])
{
primes[cnt ++] = i;
for(int j = i * 2; j <= n; j += i)
st[j] = true;
}
}
}
在遍历的i时候还是会有某些质数重复给某些合数赋值,比如12会在i=2,i=3的时候反复被筛掉。
线性筛,用最小的质因子去筛掉对应合数,时间复杂度O(n)
cpp
void sieve(int n)
{
for(int i = 2; i <= n; i ++)
{
if(!st[i])primes[cnt ++] = i;
for(int j = 0; primes[j] <= n / i; j ++)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0)break;
}
}
}
可以分析一下为什么是这样的:
因为j从小到大遍历,即质数从小到大遍历,所以primes[j]一定小于等于i的最小质因子,因为
if(i % primes[j] != 0),那么说明i的最小质因子还没有被遍历到,
if(i % primes[j] == 0),这时候说明i的最小质因子就是primes[j],直接break,
这样就可以保证primes[j] * i的最小质因子就是primes[j]
所以我们就一直在用最小质因子去筛合数,每个合数只会被遍历一次。
存下这个最小质因子就很简单了
cpp
void sieve(int n)
{
for(int i = 2; i <= n; i ++)
{
if(!st[i])primes[cnt ++] = i, p[i] = i;
for(int j = 0; primes[j] <= n / i; j ++)
{
st[primes[j] * i] = true;
p[primes[j] * i] = primes[j];
if(i % primes[j] == 0)break;
}
}
}
分解质因数
一个大于1的数可以被分解为若干个质数的乘积
从小到大找即可,下面输出质因子及个数
cpp
void work(int x)
{
for(int i = 2; i <= x / i; i ++)
{
int cnt = 0;
if(x % i == 0)
{
while(x % i == 0)
{
x /= i;
cnt ++;
}
cout<<i<<' '<<cnt<<endl;
}
}
if(x > 1)cout<<x<<' '<<1<<endl;
}