问题引入
现在有一个问题,给定一个数n,求出1~n和n互质的数的个数
- 暴力做法,for一遍,求每一个数和n的GCD,如果结果为1,即为合法答案。这种做法应该说在n比较小的时候还是可以的,如果n比较大,还不够好
欧拉函数原理
- 欧拉函数可以解决这个问题,设f为欧拉函数,f(x)的含义就是1~x与x互质的元素个数
- 首先要知道欧拉函数是一个积性函数,什么是积性函数?顾名思义就是形如
f ( x y ) = f ( x ) × f ( y ) f(xy)=f(x)\times f(y) f(xy)=f(x)×f(y)这样的就叫做积性函数,如何证明?我暂时不想看,等数论书到了再补充吧,暂时知道这是个积性函数就行,也没影响 - 那么算术基本定理是我们都知道的,就是大于一的数总可以写成
p 1 e 1 × p 2 e 2 . . . . . . p k e k p_1^{e_1}\times p_2^{e_2}......p_k^{e_k} p1e1×p2e2......pkek的形式,其中p是质数,那么
f ( p 1 e 1 × p 2 e 2 . . . . . . p k e k ) = f ( p 1 e 1 ) × . . . × f ( p k e k ) f(p_1^{e_1}\times p_2^{e_2}......p_k^{e_k})=f(p_1^{e_1})\times ...\times f(p_k^{e_k}) f(p1e1×p2e2......pkek)=f(p1e1)×...×f(pkek)
考虑p1的e1次方这个数,小于它的正整数有多少数和它互质呢?考虑有多少不互质的,显然所有p1的倍数的数都是这样的(相等不算),那么有多少个这样的数呢?具体如下
p 1 、 2 p 1 . . . p 1 e 1 − 1 p_1、2p_1...p_1^{e_1-1} p1、2p1...p1e1−1
因为
p 1 e 1 = p 1 × p 1 e 1 − 1 p_1^{e_1}=p_1\times p_1^{e_1-1} p1e1=p1×p1e1−1
所以上面一共有p1^(e1 - 1)个数,这样
f ( p 1 e 1 ) × . . . × f ( p k e k ) = p 1 e 1 − 1 × . . . × p k e k − 1 f(p_1^{e_1})\times ...\times f(p_k^{e_k})=p_1^{e_1-1}\times...\times p_k^{e_k-1} f(p1e1)×...×f(pkek)=p1e1−1×...×pkek−1 = p 1 e 1 × p 2 e 2 × . . . × p k e k × ( 1 − 1 p 1 ) × . . . × ( 1 − 1 p k ) =p_1^{e_1}\times p_2^{e_2}\times ... \times p_k^{e_k}\times (1-\frac{1}{p_1})\times ... \times (1-\frac{1}{p_k}) =p1e1×p2e2×...×pkek×(1−p11)×...×(1−pk1) = n × ( 1 − 1 p 1 ) × . . . × ( 1 − 1 p k ) =n\times (1-\frac{1}{p_1})\times ... \times (1-\frac{1}{p_k}) =n×(1−p11)×...×(1−pk1)
用数学符号表示就是
e u l e r ( n ) = n × ∏ k = 1 k ( 1 − 1 p k ) euler(n)=n\times \prod_{k=1}^k(1-\frac{1}{p_k}) euler(n)=n×k=1∏k(1−pk1)
这样就得到了欧拉函数的公式
练习
- 说多了,上道题试一试,这就是所谓的水题,用来学习算法再合适不过
- 思路就是唯一分解定理分解n,然后求欧拉函数
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
ll euler(ll n){
ll ans = n;
for(int i=2;i*i<=n;i++){
if(n % i == 0){
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n > 1) ans = ans / n * (n - 1);
return ans;
}
int main(){
ll n;
while(cin >> n && n){
cout << euler(n) << "\n";
}
return 0;
}
题目链接
然后看一下这道题,题目意思很好理解
- 首先当n为1的时候,答案为0
- 当n为2时候,答案为3
- n大于2的时候,很容易发现横纵坐标互质时候是能看见的,因为如果不互质,那就说明它处于一条直线的后面位置,前面肯定被挡住了,所以分成两半来求解,分别求欧拉函数即可,特殊情况注意一下
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int Data[MAXN];
int euler(int n){
int ans = n;
for(int i=2;i*i<=n;i++){
if(n % i == 0){
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n > 1) ans = ans / n * (n - 1);
return ans;
}
int main(){
int n;
int ans = 3;
cin >> n;
if(n == 1) cout << 0;
else if(n == 2) cout << 3;
else{
for(int i=2;i<n;i++){
ans += 2 * euler(i);
}
cout << ans;
}
return 0;
}