【数论系列】 欧拉函数

本文深入解析欧拉函数的概念,推导其计算公式,并提供三种代码实现方式:直接求解、打表求解和循环求解。通过POJ3090 Visible Lattice Points例题,展示如何利用欧拉函数解决实际问题。

目录

一. 欧拉函数概述

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是因为加上两个特殊点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿阿阿安

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

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

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

打赏作者

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

抵扣说明:

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

余额充值