【数论系列】 欧拉函数

目录

一. 欧拉函数概述

1. 基本概念

2. 公式推导

二. 代码实现

1. 直接求解

2. 打表求解

3. 循环求解

三. 例题分析

1. POJ3090 Visible Lattice Points 


一. 欧拉函数概述

1. 基本概念

        我们知道互质是指两个正整数只有一个公因数1时,它们的关系叫做互质。欧拉函数就是对于一个正整数n, 求1~n中跟n互质的数有多少个。

2. 公式推导

        举例一个数字12。12分解为最小的质因数为 12 = 2*2*3 ,也就是说2和3的倍数跟12都不是互质的,那么跟12不互质的有 12/2 + 12/3个,但是这里重复了12/6个  所以互质数目N = 12 - 12/2 - 12/3 + 12/6 = 12(1 - 1/2)(1 - 1/3)。由上述规律我们可以得到欧拉函数的公式:

euler(x) = x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…(1-1/pn)

        其中p1,p2……pn为x的所有质因数,且x是不为0的整数;注意euler(1)=1,即唯一和1互质的数就是1本身。

二. 代码实现

1. 直接求解

int Euler(int a)
{
    int ans = a;//累积ans = n*(1-1/p1)(1-1/p2)....初始化为n
    for(int j = 2;j*j<=a;j++){//求所有质因数
        if(a%j==0){//找到一个质因数
            ans = ans/j*(j - 1);//累乘(先除后乘防止溢出)
            while(a%j==0)a/=j;//把这个质因数除干净
        }
    }
    if(a>1)ans = ans/a*(a - 1);//如果最后不等于1说明剩下一个质因数累乘
    return ans;返回结果
}

2. 打表求解


int num[maxn];
memset(num,0,sizeof(num));
num[1] = 1;//1的互质数为1
void Euler(int a)
{
    for(int i = 2;i<=a;i++){
        if(!num[i]){//这个数没标记过说明这个数是一些数字的质因数
            for(int j = i;j<=a;j+=i){//他的倍数都是以他为质因数
                if(!num[j])num[j] = j;
                num[j] = num[j]/i*(i-1);//累乘
            }
        }
    }
}

3. 循环求解

int n;
scanf("%d",&n);
for(int i = 2;i<n;i++){
  if(n%i==0&&!judge[i]){
    for(int j = i;j<=n;j+=i)judge[j] = 1;
   }
}
int sum = 0;
for(int i = 1;i<n;i++)
{
   if(!judge[i])sum++;
}
printf("%d\n",sum);

三. 例题分析

1. POJ3090 Visible Lattice Points 

在n*n的平面内找有几个点满足,与原点连线不经过其他任何一个点?

        所有不经过其他点的连线,在该点xy相互是互质的。所以首先求出所有1~n的欧拉函数值,注意这里行和列是分别计算的!累积起所有的1~k的欧拉函数值即为答案,但是要注意(0,1)和(1,0)这两个特殊值要加上。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long int LL;
const int maxn = 1000 + 5;
LL num[maxn];//储存每个数字1~k的欧拉函数值
LL Sum[maxn];//求前缀和预处理一下子
int n;
void Euler(int n)//欧拉函数打表
{
    num[1] = 1;
    for(int i = 2;i<=maxn;i++){
        if(!num[i]){
            for(int j = i;j<=maxn;j+=i){
                if(!num[j])num[j] = j;
                num[j] = num[j]/i*(i - 1);
            }
        }
    }
}
void pretation()//预处理
{
    Sum[1] = 1;
    for(int i = 2;i<=maxn;i++){
        Sum[i] = Sum[i - 1] + num[i];
    }
}
int main()
{
    memset(num,0,sizeof(num));
    memset(Sum,0,sizeof(Sum));
    Euler(maxn);
    pretation();
    int T;
    scanf("%d",&T);
    int t = 0;
    while(T--){
        t++;
        scanf("%d",&n);
        printf("%d %d %lld\n",t,n,Sum[n]*2  - 1 +2);//行列分别求所以sum[n]*2 
    }
    return 0;                                      //-1是因为(1,1)求了两遍,减去一个
}                                                  //+2是因为加上两个特殊点
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿阿阿安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值