Linear Sieve Algorithms
Linear sieve algorithms that optimized number sieving problems
Linear Sieve Algorithms
对于一个函数 ,求解它有若干种方法:
-
对于单个 的求值,一般通过定义式直接构造。
-
对于一段区间的求值 ,考虑利用函数的性质进行快速筛选。
质数筛法
- --埃氏筛
最经典的筛法,时间复杂度 。
对于每一个质数,把它的倍数标记为合数
vector<bool> npr; // is composite / not prime
void Esth(int n) {
npr.assign(n + 1, false);
npr[0] = npr[1] = true;
int m = sqrt(n);
for (int i = 2; i <= m; ++i)
if (!npr[i])
for (int j = i * i; j <= n; j += i)
npr[j] = true;
}
- 线性筛(欧拉筛) 可以在 的时间复杂度里,筛出 ~ 的所有函数值,因为每个数的函数值只会被计算一次,而且是 转移。
关于正确性:首先与埃氏筛一样,对于每一个质数,都把它的倍数筛为了合数。
时间上的优秀:注意到 操作,因为我们是从小到大枚举质数,所以一个数只会被它的最小质因子筛去,当 已经被 筛过了, 其他任意一个质数还是 的倍数,肯定已经被 筛过了。
筛质数的同时,也可以筛出最小质因子
vector<bool> npr; // is composite / not prime
vector<int> primes, low;
void Linear(int n) {
npr.assign(n + 1, false);
low.assign(n + 1, 0);
primes.clear();
npr[0] = npr[1] = true;
for (int i = 2; i <= n; ++i) {
if (!npr[i]) primes.push_back(i), low[i] = i;
for (int pr : primes) {
int k = i * pr;
if (k > n) break;
npr[k] = true;
low[k] = pr;
if (i % pr == 0) break;
}
}
}
积性函数
定义 为数论函数。 积性函数:对于任意互质的 ,有 。 完全积性函数:对于任意的 ,有 。
根据第一条定义:把 按照算数基本定理拆分, ,有 ,同时对于单个质数 ,一般可以在 或 的时间内求出。
考虑如何通过线性筛筛出,显然要能运用算数基本定理,我们需要质数,所以只需在筛质数的基础上稍作修改。对于 是质数的情况,一般函数值比较显然。设 ,根据线性筛的原理, 是 的的最小质因子。当 是 的质因子时(即 ),假设 中 的个数为 ,那么令 ,那么 与 互质,则 ;如果 不是 的质因子,那么 与 互质,则 ,对于 最小质因子的指数 ,可以一边筛质数一边筛出来。
Euler's totient function (欧拉函数)
定义:小于等于 的数中与 互质的数的个数。
通项公式:
为 的不同质因数。
int phi(int n) { //根据基础定义,求单个数的欧拉函数
int res = n, m = sqrt(n);
for (int i = 2; i <= m; ++i)
if (n % i == 0) {
res -= res / i;
while (n % i == 0) n /= i;
}
if (n > 1) res -= res / n;
return res;
}
线性筛法求区间欧拉函数:
vector<bool> npr;
vector<int> primes, phi;
void Euler(int n) { //欧拉函数
npr.assign(n + 1, false);
phi.assign(n + 1, 0);
primes.clear();
phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!npr[i]) primes.push_back(i), phi[i] = i - 1;
for (int pr : primes) {
int k = i * pr;
if (k > n) break;
npr[k] = true;
if (i % pr == 0) {
phi[k] = phi[i] * pr;
break;
} else phi[k] = phi[i] * (pr - 1);
}
}
}
莫比乌斯函数
vector<bool> npr;
vector<int> primes, miu;
void Mobius(int n) { //莫比乌斯函数
npr.assign(n + 1, false);
miu.assign(n + 1, 0);
primes.clear();
miu[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!npr[i]) primes.push_back(i), miu[i] = -1;
for (int pr : primes) {
int k = i * pr;
if (k > n) break;
npr[k] = true;
if (i % pr == 0) { miu[k] = 0; break; }
else miu[k] = -miu[i];
}
}
}
约数个数
vector<bool> npr;
vector<int> primes, d;
void NumDivisors(int n) { //约数个数
npr.assign(n + 1, false);
d.assign(n + 1, 0);
primes.clear();
d[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!npr[i]) primes.push_back(i), d[i] = 2;
for (int pr : primes) {
int k = i * pr;
if (k > n) break;
npr[k] = true;
if (i % pr == 0) {
d[k] = 2 * d[i] - d[i / pr];
break;
} else d[k] = 2 * d[i];
}
}
}
正约数的和
vector<bool> npr;
vector<int> primes, s;
void SumDivisors(int n) { //约数和
npr.assign(n + 1, false);
s.assign(n + 1, 0);
primes.clear();
s[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!npr[i]) primes.push_back(i), s[i] = i + 1;
for (int pr : primes) {
int k = i * pr;
if (k > n) break;
npr[k] = true;
if (i % pr == 0) {
s[k] = s[i] + (s[i] - s[i / pr]) * pr;
break;
} else s[k] = s[i] * (pr + 1);
}
}
}