传统的筛法筛素数由于会重复标记所以会出现多余的操作,导致复杂度上升。这里有一个线性筛素数的方法可以在O(n)的复杂度内将所有非素数标记出来。
首先 ,一个非素数可以看作是多个素数的乘积,如10 可以看作2和5,14 看作2,7, 25==5*5 ,36=2*3*3*2
那么一个数一定由一个最小的质因子,乘以某个数的来的。并且这个关系一定是一一对应的。 可以自己画表尝试验证一下。
那么我们从2开始筛,小于=2的质数有2 ,所以 得到2*2 = 4 标记为非质数。 然后是3 小于等于3的有 2 ,3 所以得到 6 与 9 为非素数。 然后是4*2 = 8 , 5*2=10 5*3=15 5 * 5 = 25;一直推到下去,即,每个数乘比他小的素数即可。并且那个素数如果是乘积数的质因子。则跳出本次筛素数。因为,我们要做到一一对应。如果继续筛下去,会重复。 就如上面举得例子,4*3 =12 6*2 = 12 这样就被筛了两次。所以不行。
#include<stdio.h>
const int maxs = 1e6+1;
bool vis[maxs+10];
int prime[maxs+10];
int phi[maxs+10];
void Elu()
{
phi[1]=1;
int top = 0;
for(int i = 2; i<=maxs;i++)
{
if(vis[i]==0)//vis[i]== 0 说明该数为质数,则phi[i]=i-1;
{
prime[top++] = i;
phi[i] = i-1;
}
for(int j = 0 ;j<top && i*prime[j]<maxs;j++) // i*prime[j] <maxs 防止越界
{
vis[prime[j]*i] = 1; //筛出一个非素数
if(i%prime[j]==0)
{
phi[i*prime[j]] = phi[i] * prime[j]; //此时prime[j]为i的一个质因子。由公式得到phi[i]的值
break;
}
else
{
phi[i*prime[j]] = phi[i] * phi[prime[j]] ;//此时i与prime[j]互质 由公式得到phi[i];
}
}
}
return ;
}
int get_phi(int n)
{
int ans = n;
for(int i = 2; i*i <= n;i++)
{
if(n%i==0)
{
ans = (ans/i) * (i-1); //化简可得 ans = ans(1-1/p);
while(n%i==0)
{
n=n/i; //将相同的质因子去掉。
}
}
}
if(n!=1)
{
ans = (ans/n) * (n-1); //如果剩下的n非1,说明仍残留一个数,此时n如上面的i一样看待。
}
return ans;
}
int main()
{
Elu();
for(int i = 1;i<=20;i++)
{
printf("%d phi[%d]=%d get_pgi(%d) == %d\n",prime[i],i,phi[i],i,get_phi(i));
}
}
以上为代码段,一般用线性筛法是用来打欧拉函数表的。所以我就放一块了。
接下来说说欧拉函数是什么梗。
欧拉函数就是给你一个n 返回 [1 n]中 与n最大公因数为1的数的个数。
普通求解方法由唯一定理求解。即n个质因子相乘会得到一个合数。
设第i个质因子 为 则 [1,N]中 Pi的倍数为 N/Pi 。 由容斥定理得到。 满足欧拉函数定义的数的个数为
emmm.我也不知道这个公式这个怎么用,只会写成这样了。 个数 = N -(N/P1+N/P2+N/P3+N/P4.....-N/P1P2 - N/P1P3 - N/P2P3 .......+N/P1P2P3P4P5....Pn);
反正推导出来结果就是 个数 = n(1-1/P1)*(1-1/P2)*(1-1/P3)......;
直接求欧拉函数的复杂度为O(质因子个数)。
这里我们就设欧拉函数为phi(n)吧。
如果是由线性筛法的话,我们又要想到 一个质数的欧拉函数的值一定为质数的值减1. 比如phi(13)=12;
并且,m1,m2,两个数如果互质的话,phi(m1*m2)== phi(m1)*phi(m2);
并且 由phi (n) = n(1-1/P1)*(1-1/P2)*(1-1/P3)......;可知,当n乘以一个本身的质因子的时候,质因子个数是不会变得。
那么 phi(n*Pi)=n*Pi*(1-1/P1)*(1-1/P2)*(1-1/P3)
所以可以由这些性质筛出范围内的素数。