跳到主要内容

欧拉函数

求单个欧拉函数

φ(n)表示1~n 中与n互质的数的个数。 ψ(n)=n(11p1)(11p2)(11pk)\psi(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})···(1-\frac{1}{p_k}) 其中:n=p1c1p2c2pkckn=p_1^{c1}p_2^{c2}···p_k^{ck}

  • 为避免浮点数除法,编程时采用另一个形式编写代码φ(n)=n(p11p1)(p21p2)(pk1pk)\varphi \left( n \right) =n\left( \frac{p_1-1}{p_1} \right) \left( \frac{p_2-1}{p_2} \right) \cdots \left( \frac{p_k-1}{p_k} \right)
  • 可用先除后乘的方式res = res / n * (n-1)避免结果溢出
  • 例子:φ(6)=2\varphi(6)=2,因为11~66中只有115566互质
  • 可用容斥原理证明:φ(n)=ninpi+i,jnpipji,j,knpipjpk+\varphi \left( n \right) =n-\sum_i{\frac{n}{p_i}+\sum_{i,j}{\frac{n}{p_i p_j}}-\sum_{i,j,k}{\frac{n}{p_i p_j p_k}}}+\cdots
  • 时间复杂度O(n)O(\sqrt{n})

代码

int phi(int x)
{
int res = x;
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
{
res = res / i * (i - 1);
while (x % i == 0) x /= i;
}
if (x > 1) res = res / x * (x - 1);

return res;
}

筛法求多个欧拉函数

  • 核心思想:φ(n)\varphi(n)nn的质指数大小无关
    • nn是质数时,φ(n)=n1\varphi(n)=n-1
    • nmodp=0n \mod p = 0时,φ(pn)=pφ(n)\varphi(pn)=p\varphi(n),因为质数pp只影响pnpn的质指数cpc_p
    • nmodp0n \mod p \ne 0时,φ(pn)=pφ(n)(11p)=(p1)φ(n)\varphi(pn)=p\varphi(n)(1-\frac{1}{p})=(p-1)\varphi(n),因为质数pp影响了pnpn展开项数,多了一项p1p^1
  • 借助线性筛法求欧拉函数,时间复杂度O(n)O(n)

代码

int primes[N], cnt;     // primes[]存储所有素数
int euler[N]; // 存储每个数的欧拉函数
bool st[N]; // st[x]存储x是否被筛掉(合数标记)


void get_eulers(int n)
{
euler[1] = 1;
for (int i = 2; i <= n; i ++ )
{
if (!st[i])
{
// 质数
primes[cnt ++ ] = i;
euler[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; j ++ )
{
int t = primes[j] * i;
st[t] = true;
if (i % primes[j] == 0)
{
euler[t] = euler[i] * primes[j];
break;
}
euler[t] = euler[i] * (primes[j] - 1);
}
}
}