首页 > 其他分享 >欧拉函数(新)

欧拉函数(新)

时间:2024-05-25 22:00:29浏览次数:16  
标签:frac 函数 int 质数 varphi times 欧拉

欧拉函数 \(\varphi\) 的定义,\(\varphi(i)\) 表示从 \([1, i]\) 之间和 \(i\) 互质的数的数量 ( \(a\) 和 \(b\) 互质即 \(\gcd(a, b) = 1\))。欧拉函数是积性函数,例如 \(a, b\) 都为质数,那么 \(φ(ab) = φ(a) \times φ(b)\),递推式为

\[φ(ab) = \frac {φ(a) \times φ(b) \times \gcd(a,b)}{φ(\gcd(a,b))} \]

(证明暂时搁置)

设一个数 \(N\) 可得

\[N = p_1^{b_1} \times p_{2}^{b_2} \times p_{3}^{b_3} \times \ldots \times p_{k}^{b_k} \quad p为质数 \]

因为

\[\displaylines{ \varphi(N) = \varphi(p_1^{b_1}) \times \varphi(p_2^{b_2}) \times \varphi(p_3^{b_3}) \times \ldots \times \varphi(p_k^{b_k}) \\ \varphi(p^b) = p^b - p^{b-1} } \]

还因为 \([1, p^b]\) 一共有 \(p^b\) 个数,不和 \(p^b\) 互质的数有 \(1 \times p, 2 \times p, 3 \times p, \ldots, p^{b-1} \times p\) , 总共 \(p^{b-1}\) 个, 剩下的就是满足要求的即 \(p^b - p^{b-1}\) 个数。

\[φ(p^b) = p^b \times (1 - \frac{1}{p}) \]

可得

\[\varphi(N) = p_1^{b_1} \times (1 - \frac{1}{p_1}) * p_2^{b_2} * (1 - \frac{1}{p_2}) \times \ldots p_k^{b_k} * (1 - \frac{1}{p_k}) \]

就等于

\[\varphi(N) = (p_1^{b_1} \times p_2^{b_2} \times \ldots * p_k^{b_k}) \times (1 - \frac{1}{p_1}) * (1 - \frac{1}{p_2}) \times \ldots \times (1 - \frac{1}{pk}) \]

又因为

\[N = p1^b1 * p2^b2 * ... * pk^bk \]

可得

\[\varphi(N) = N * (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times \ldots \times (1 - \frac{1}{p_k}) \]

就可以通过分解 \(N\) 的质因数求出来 \(\varphi(N)\),由此也可以看出,一个数的欧拉函数的大小和质数的次幂无关。

试除法分解质因数是 \(O(\sqrt n)\) 的, 所以求 \(\varphi(N)\) 也就是 \(O(\sqrt n)\) 的
具体见代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

int n, m;

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        cin >> n;
        int res = n;
        for (int i = 2; i <= n / i; i ++ )
        {
            if (n % i == 0) 
            {
                res = res / i * (i - 1); // 相当于res * (1 - 1 / i), 这样是为了防止出现小数, 下取整没了, 最主要的就是这里
                while (n % i == 0) n /= i;
            }
        }
        if (n != 1) res = res / n * (n - 1); // 这里不要忘记
        cout << res << endl;
    }
    return 0;
}

一个数欧拉函数的大小和质因数次幂无关

筛法求欧拉函数

\(O(n)\)
这里写的注释很好,就不多重打了。
是用线性筛顺便筛出欧拉函数,首先,线性筛可以筛出质数 p,质数的欧拉函数很好求,因为一个质数在 \([1, p]\) 中除了 p 本身以外,其他所有数都与它互质,所以 \(\varphi(p) = p - 1\)。
而对于筛掉的数,我们可以知道,筛掉的数是用这个数 u 的最小质因子 p 筛去的,唉?质因子是质数吧,按算法运行顺序来说, u 是由 \(p \times i\) 得到的,那么 i 是整数,且肯定比 u 小,按理说,它的欧拉函数我已经求出来了。而 \(\varphi(p) = p - 1\),我们还知道一个等式。

\[\varphi(ab) = \frac {φ(a) \times φ(b) \times \gcd(a,b)}{φ(\gcd(a,b))} \]

那么就可以得出来了

\[\varphi(u) = \varphi(i \times p) = \frac {\varphi(p) \times \varphi(i) \times \gcd(p,i)}{\varphi(\gcd(i,p))} \]

其中因为 \(p\) 是质数,而 \(p <= i\) ,并且 p 是 i 的质因子,所以 \(\gcd(i,p) = p\),所以 \(\varphi(\gcd(p,i)) = \varphi(p)\)
所以有下式

\[\varphi(u) = \varphi(i \times p) = {p \times \varphi(i)} \]

在注释里还有一种解释方法,这里就不说了。

/*
    线性筛可以求出很多附加的东西
    具体会在代码里写注释
*/

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 1000010;

int n;
int primes[N], cnt;
bool st[N];
int phi[N]; // phi[i] 是i的欧拉函数
LL sum;

int main()
{
    cin >> n;
    phi[1] = 1; // 1的欧拉函数是1, 需要手动写上
    for (int i = 2; i <= n; i ++ )
    {
        if (st[i] == 0)
        {
            primes[ ++ cnt] = i;
            sum += phi[i] = i - 1; // 首先如果i是质数, 质数和所有数都互质(除了它自己), 那么对于质数i的φ, 就是i - 1
        }
        for (int j = 1; primes[j] <= n / i; j ++ )
        {
            st[i * primes[j]] = true;
            if (i % primes[j] == 0) // 如果i % pj == 0 那么pj就是i的最小质因数(这点在线性筛里提到过)
            { // 说明i的质因数包括pj, 那么φ(i)里面包括 (1 - 1/pj), 一个数欧拉函数的大小和其质因数的次幂无关, 根据φ(N) = N * (1 - 1/p1) * (1 - 1/p2) * ... * (1 - 1/pk);
                sum += phi[i * primes[j]] = phi[i] * primes[j]; // pj * i 比 i 只多了一个pj而且pj还在i的质因数里面, 那么 φ(i*pj)只比φ(i)多一个pj 也就是 φ(i*pj) = φ(i) * pj
                break;
            }
            sum += phi[i * primes[j]] = phi[i] * (primes[j] - 1); // 和上面同理, 但是pj不是i的质因数, 所以φ(i) 不包含 (1 - 1/pj), φ(pj*i)需要加上这个 
        }
    }
    cout << sum + 1 << endl;
    
    return 0;
}

标签:frac,函数,int,质数,varphi,times,欧拉
From: https://www.cnblogs.com/blind5883/p/18213056

相关文章

  • Java SE入门及基础(54)& 函数式接口
    目录1.什么是函数式接口函数式接口示例示例2.函数式编程示例3.Lambda表达式延迟执行应用场景示例4.Consumer接口解释说明示例5.BiConsumer接口解释说明示例6.Predicate接口解释说明示例练习7.Function接口解释说明示例练习1.什么是函数......
  • Lambda表达式的使用以及函数式接口
    目录Lambda表达式1.什么是Lambda表达式2.Lambda表达式的使用3.Lambda表达式的写法规则(以以上例子来说)4.要注意的要点方法引用 函数式接口1.什么是函数式接口2.函数是接口的自定义使用3.JDK自带的函数式接口总结 Lambda表达式1.什么是Lambda表达式Lambda表达......
  • C++入门(3) 指针和引用的区别|引用的本质|引用小结|inline函数|缺省函数
    一,引用引用和指针的区别1,从语法规则上讲指针变量存储某个实例(变量或者对象)的地址;引用是某个实例的别名程序为指针变量分配内存空间;不为引用分配内存空间指针变量的值可以改变;引用一旦初始化就无法改变指针变量可以为NULL;但是没有空引用指针作为形参需要判断是否为空;引用......
  • 一个模板元函数来检查一个类是否有一个特定的成员
    通过创建一个模板元函数来检查一个类是否有一个特定的成员。以下是一个例子:#include<type_traits>template<typenameT,typename=void>structhas_type_member:std::false_type{};template<typenameT>structhas_type_member<T,std::void_t<typenameT::typ......
  • 六.函数
    函数一.格式格式:def函数名(参数1,参数2...)代码:defqh(a,b): returna+bprint(qh(2,3))#输出:5二.递归1.自己调用自己2.有边界(结束条件)代码:defqh(a):ifa==1:return1returna+qh(a-1)n=int(input())#n==10print(qh(n))三.递归过程......
  • C++基础知识学习笔记(5)——函数
    学习参考:https://www.bilibili.com/video/BV1et411b73Z?p=95&spm_id_from=pageDriver&vd_source=cc561849591f6a210152150b2493f6f3函数函数的默认参数可以为形参提供默认值。intadd(inta,intb=1,intc=2){ returna+b+c;}intmain(){ cout<<(add(1,3,......
  • 函数和数组的混合使用例子
    目录写两个函数,分别求两个数的最大公约数和最小公倍数写一个函数,使一个3x3的整形二维数组转置(行列转换)写一个函数打印杨辉三角扫雷游戏学习完了函数和数组,我们来进行简单的应用吧~写两个函数,分别求两个数的最大公约数和最小公倍数   一般我们求最大公约数可以使用......
  • C语言中的函数(2)
    目录前言函数的调用和声明函数的嵌套调用 函数的链式访问函数的递归调用递归求n的阶乘递归计算斐波那契数static和extern作用域和生命周期变量存储方式作用static修饰局部变量extern的使用static修饰全局变量static修饰函数函数的要求      内聚性强......
  • 数据库函数下拉式求和
    问题:如何用Dsum实现单条件求和的下拉函数解决:=DSUM($C$1:$E$9,D$1,$K$1:$K2)-SUM(L$1:L1)Dsum公式在第2行实现的是股票名称为A的求和结果;到第3行时变成股票名称为A和B的求和结果,这时需要减掉上一个单元格的数据;到第4行则需要减掉上两个单元格求和的数据。使用Sum(L$1:L1)......
  • 构造函数
    类成员初始化方式:1、通过构造函数的参数列表初始化。2、在构造函数中赋值完成初始化。//1、通过构造和函数的参数列表初始化Seles_data::Sales_data(constSales_data&sa){ this->bookNo=sa.bookNo; this->revenue=sa.revenue; this->units_sold=sa.units_sold;}......