线性筛
2015-07-08 16:48
411 查看
今天在看了各种以及听了各种之后终于算是了解线性筛了…
虽然都是一些很基本的应用但还是觉得各种强大…
果然这段代码最鬼畜的就是这句,今天在翻其他东西的时候偶然发现了这个:
这行代码神奇地保证了每个合数只会被它的最小素因子筛掉,就把复杂度降到了O(N)O(N)
接下来是证明这个算法正确性的说明:
prime[]prime[]数组中的素数是递增的,当ii能整除prime[j]prime[j],那么i∗prime[j+1]i*prime[j+1]这个合数肯定被prime[j]prime[j]乘以某个数筛掉。
因为ii中含有prime[j]prime[j],prime[j]prime[j]比prime[j+1]prime[j+1]小,即i=k∗prime[j]i=k*prime[j],那么i∗prime[j+1]=(k∗prime[j])∗prime[j+1]=k′∗prime[j]i*prime[j+1]=(k*prime[j])*prime[j+1]=k'*prime[j],接下去的素数同理。所以不用筛下去了。因此,在满足ii%prime[j]==0这个条件之前以及第一次满足改条件时,prime[j]prime[j]必定是prime[j]∗iprime[j]*i的最小因子
φ(x)=x−1 <x∈prime>φ(x)=x-1~
φ(x)=xΠ(1−1pi)=Π(pi−pai−1i)φ(x)=xΠ(1-\frac{1}{p_i})=Π(p_i-p_i^{a_i-1})
除此只外,欧拉函数还是积性函数,即φ(xy)=φ(x)φ(y) <gcd(x,y)=1>φ(xy)=φ(x)φ(y)~
虽然不需要筛法求欧拉函数了,但根据公式,还是需要筛素数
以下是筛素数之后的代码
f(x)=Π(ai+1)f(x)=Π(a_i+1)
f(x)=2 <x∈prime>f(x)=2~
发现f(x)f(x)也是积性函数,即f(xy)=f(x)f(y) <gcd(x,y)=1>f(xy)=f(x)f(y)~
当prime[j]prime[j]是ii的约数时,i∗prime[j]i*prime[j]就相当于ii多了一个最小素因子,根据之前的公式,所以转移如上。
当ii与prime[j]prime[j]互质时,由积性知f[]转移如上。
对于a[]的转移,我是这样理解的:
我们先假设i∗prime[j]i*prime[j]的最小素因子个数为1。
如果i∗prime[j]i*prime[j]的最小素因子是由ii提供的话,我们马上就会枚举到它的最小素因子,然后把a[i∗prime[j]]a[i*prime[j]]修改为正确的值。
否则i∗prime[j]i*prime[j]的最小素因子就是prime[j]prime[j]且不被ii包含。这是因为首先i mod prime[j]!=0i~mod~prime[j]!=0;其次,假设i∗prime[j]i*prime[j]的最小素因子在ii中,那么肯定早就
μ(x)=−1k<x=Πki=1pi,pi的次数为1>μ(x)=-1^k
μ(x)=0<其他情况>μ(x)=0<其他情况>
莫比乌斯函数同样是积性函数,即μ(xy)=μ(x)μ(y) <gcd(x,y)=1>μ(xy)=μ(x)μ(y)~。
参考的内容:
线性筛(欧拉筛)
【数论内容】线性筛素数,线性筛欧拉函数,求前N个数的约数个数
莫比乌斯反演ppt by PoPoQQQ
xiaohao1大神的讲解与莫比乌斯反演pdf
虽然都是一些很基本的应用但还是觉得各种强大…
线性筛素数
代码
int tot_prime, prime[maxn]; bool vist[maxn]; void get_prime(){ for(int i = 2; i <= n; ++i){ if(!vist[i]) prime[++tot_prime] = i; for(int j = 1; i * prime[j] <= n && j <= tot_prime; ++j){ vist[i*prime[j]] = true; if(i % prime[j] == 0) break; } }
一些解释
第一次看到的时候就有一句话觉得很鬼畜…if(i % prime[j] == 0) break;
果然这段代码最鬼畜的就是这句,今天在翻其他东西的时候偶然发现了这个:
这行代码神奇地保证了每个合数只会被它的最小素因子筛掉,就把复杂度降到了O(N)O(N)
接下来是证明这个算法正确性的说明:
prime[]prime[]数组中的素数是递增的,当ii能整除prime[j]prime[j],那么i∗prime[j+1]i*prime[j+1]这个合数肯定被prime[j]prime[j]乘以某个数筛掉。
因为ii中含有prime[j]prime[j],prime[j]prime[j]比prime[j+1]prime[j+1]小,即i=k∗prime[j]i=k*prime[j],那么i∗prime[j+1]=(k∗prime[j])∗prime[j+1]=k′∗prime[j]i*prime[j+1]=(k*prime[j])*prime[j+1]=k'*prime[j],接下去的素数同理。所以不用筛下去了。因此,在满足ii%prime[j]==0这个条件之前以及第一次满足改条件时,prime[j]prime[j]必定是prime[j]∗iprime[j]*i的最小因子
求欧拉函数
相关公式
x=Πpaiix=Πp_i^{a_i}φ(x)=x−1 <x∈prime>φ(x)=x-1~
φ(x)=xΠ(1−1pi)=Π(pi−pai−1i)φ(x)=xΠ(1-\frac{1}{p_i})=Π(p_i-p_i^{a_i-1})
除此只外,欧拉函数还是积性函数,即φ(xy)=φ(x)φ(y) <gcd(x,y)=1>φ(xy)=φ(x)φ(y)~
代码
利用线性筛可以在线性时间内求得phi[i]int tot_prime, prime[maxn], phi[maxn]; bool vist[maxn]; void get_prime(){ phi[1] = 1; for(int i = 2; i <= n; ++i){ if(!vist[i]) prime[++tot_prime] = i, phi[i] = i - 1; for(int j = 1; i * prime[j] <= n && j <= tot_prime; ++j){ vist[i*prime[j]] = true; if(i % prime[j] == 0){ phi[i*prime[j]] = phi[i] * prime[j]; break; } else phi[i*prime[j]] = phi[i] * prime[j]; } }
what’s more
但有时并不需要求1...x1...x的所有欧拉函数值,很多时候我们要求的都是一个比较大的数字x(x∈[1,109])x(x∈[1,10^9])的欧拉函数值虽然不需要筛法求欧拉函数了,但根据公式,还是需要筛素数
以下是筛素数之后的代码
int phi(int x){ int rtn = 1, cpy_x = x; for(int i = 1; prime[i] * prime[i] <= cpy_x && i <= tot_prime; ++i){ int temp = 1; while(x % prime[i] == 0){ x /= prime[i]; temp *= prime[i]; } if(temp > 1) rtn *= temp - temp / prime[i]; } if(x > 1) rtn *= (x - 1); // 还剩下一个很大的质数 return rtn; }
求约数的个数
相关公式
设f(x)f(x)为xx的约数的个数,还是把xx表示成x=Πpaiix=Πp_i^{a_i}的形式f(x)=Π(ai+1)f(x)=Π(a_i+1)
f(x)=2 <x∈prime>f(x)=2~
发现f(x)f(x)也是积性函数,即f(xy)=f(x)f(y) <gcd(x,y)=1>f(xy)=f(x)f(y)~
代码
为了方便,让a[i]a[i]表示xx最小素数因子的个数int tot_prime, prime[maxn]; int f[maxn], a[maxn]; bool vist[maxn]; void get_f(){ f[1] = 1; for(int i = 2; i <= n; ++i){ if(!vist[i]) prime[++tot_prime] = i, f[i] = 2, a[i] = 1; for(int j = 1; i * prime[j] <= n && j <= tot_prime; ++j){ vist[i*prime[j]] = true; if(i % prime[j] == 0){ f[i*prime[j]] = f[i] / (a[i] + 1) * (a[i] + 2); a[i*prime[j]] = a[i] + 1; break; } else f[i*prime[j]] = f[i] * f[prime[j]], a[i*prime[j]] = 1; } } }
一些解释
if(i % prime[j] == 0){ f[i*prime[j]] = f[i] / (a[i] + 1) * (a[i] + 2); a[i*prime[j]] = a[i] + 1; break; }
当prime[j]prime[j]是ii的约数时,i∗prime[j]i*prime[j]就相当于ii多了一个最小素因子,根据之前的公式,所以转移如上。
else f[i*prime[j]] = f[i] * f[prime[j]], a[i*prime[j]] = 1;
当ii与prime[j]prime[j]互质时,由积性知f[]转移如上。
对于a[]的转移,我是这样理解的:
我们先假设i∗prime[j]i*prime[j]的最小素因子个数为1。
如果i∗prime[j]i*prime[j]的最小素因子是由ii提供的话,我们马上就会枚举到它的最小素因子,然后把a[i∗prime[j]]a[i*prime[j]]修改为正确的值。
否则i∗prime[j]i*prime[j]的最小素因子就是prime[j]prime[j]且不被ii包含。这是因为首先i mod prime[j]!=0i~mod~prime[j]!=0;其次,假设i∗prime[j]i*prime[j]的最小素因子在ii中,那么肯定早就
break了,我们就不可能枚举到prime[j]prime[j]。
求莫比乌斯函数
相关公式
μ(x)=1 <x=1>μ(x)=1~μ(x)=−1k<x=Πki=1pi,pi的次数为1>μ(x)=-1^k
μ(x)=0<其他情况>μ(x)=0<其他情况>
莫比乌斯函数同样是积性函数,即μ(xy)=μ(x)μ(y) <gcd(x,y)=1>μ(xy)=μ(x)μ(y)~。
代码
mu[1] = 1; for(int i = 2; i <= n; ++i){ if(!vist[i]) prime[++tot_prime] = i, mu[i] = -1; for(int j = 1; i * prime[j] <= n && j <= tot_prime; ++j) vist[i*prime[j]] = true; if(i % prime[j] == 0){ mu[i*prime[j]] = 0; break; } else mu[i*prime[j]] = mu[i] * mu[prime[j]]; }
参考的内容:
线性筛(欧拉筛)
【数论内容】线性筛素数,线性筛欧拉函数,求前N个数的约数个数
莫比乌斯反演ppt by PoPoQQQ
xiaohao1大神的讲解与莫比乌斯反演pdf
相关文章推荐
- 获取键盘的值
- 【英文分词】Stemming Segmentation,基于词干分词
- CNN卷积神经网络
- Block 好好运用
- wparam和lparam
- Spring事务配置的五种方式
- 自定义圆角矩形---BitmapShader
- [SQL入门级] 接上篇,继续查询
- sublime快捷键
- 使用git pull文件时和本地文件冲突怎么办?
- 【LeetCode 8_字符串_实现】String to Integer (atoi)
- 使用PowerDesigner 设计SQL Server 数据库
- AngularJS--directive
- 分布式文件系统FastDFS架构剖析
- ulimit -----修改linux的软硬件限制文件
- 怎样管理C++类中的指针成员 和 简单的c++智能指针使用的例子
- Flask-Mail邮件的配置以及发送附件的方法
- 一次性通过高级信息系统项目管理师经验总结
- java 不用科学计数法显示Double类型和Long类型
- 看到比较好的webservice文章