知识储备
1.任何一个大于1的自然数都能被唯一分解有限个质数的乘积,如 X=P1 * a1 +P2 * a2······Pn * an 其中P为质数,a为指数.
2. 对于一个合数x,必有一个范围在2~√x 的因数。
试除法
int prime(int n)
{
for(int i=2;i*i<=n;i++)
if(!(n%i))
return 0;
return 1;
}///时间复杂度o(n√n)
埃拉托色尼筛法(Eratosthenes)
void primes(int n)
{
memset(v,0,sizeof(v));//假设全是素数,无合数标记
for(int i=2;i<=n;i++)
{
if(!v[i])
{
prime[++cnt]=i;
for(int j=i*i;j<=n;j+=i)
v[j]=1;
}
}
}
时间复杂度:O(∑ N/p ) =O(N loglogN ),p为小于N的质数 。接近线性效率,而且比较起线性筛是更灵活的。
线性筛
int vis[100005], prime[100005], cnt;
int main()
{
for(int i = 2;i <= 100005;i++)
{
if(!vis[i])prime[cnt++] = i;
for(j = 0;j < cnt;j++)
{
if(i * prime[j]>100005)break;
vis[i*prime[j]]=true;
if(i%prime[j]==0) break;
}
}
}
2 | 2 |
---|---|
3 | 2 * 3 , 3 *3 |
4 | 2 * 4, 3 * 4 |
5 | 2 * 5 , 3 * 5 , 5 * 5 |
6 | 2 * 6 , 3 * 6 , 5 * 6 |
线性筛顾名思义就是所有的数只遍历一遍,保证这个成立的条件就是每个合数只被自己最小质因数筛掉,就可以了。下面解释怎么筛。
我们先知道几个前提,prime数组是递增的数组,也就是说你在prime数组找合数的第一个质因数就是最小的质因数。
黑体部分是没有if(i%prime[j]==0) break;这一步时多筛的数,可以看到,不加的情况下,12被遍历到4筛了一遍,又被遍历到6筛了一遍。被4筛可不是被他的最小质因数晒掉的,被6才是。
在i是质数时 if(i%prime[j]==0) break;是不需要考虑的。prime数组中已有的数一定比这个i小,所以可以保证每个被筛去的数一定是被小于i的质因数筛掉的。如筛5时:10,15,25都保证了被最小质因数2,3,5筛掉的。
在i为合数时,比如35,可以写作5*7,可以筛掉2 * 5 * 7,3 * 5 * 7,5 * 5 * 7,但你如果是筛7 * 5 * 7,那后头5 * 7 * 7就又会被筛一次了 。合数可以写作两个质数之积,如果prime[j]大于小得那个质数的话,那不就不是被最小质因数筛掉的了吗?