zoukankan      html  css  js  c++  java
  • 【数论】欧拉函数

    欧拉函数

    欧拉函数的定义

    对于正整数n,小于且与n互质的正整数(包括1)的个数,记作(phi(N))

    易得,(phi(质数)=质数-1)
    同时规定,(phi(1)=1)

    欧拉函数的计算

    就拿8来举例,小于8且与8互质的正整数有1,3,5,7,共4个。

    因而,(phi(8)=4)

    (phi(120))的值是多少?

    (phi(120)=120 imes(1-frac{1}{2}) imes(1-frac{1}{3}) imes(1-frac{1}{5})=32)

    为啥是这样子计算呢?

    首先,要承认的两点是,

    1. 给定一个数字n,在n这个范围内(1比较特殊)去求n的互质的数字是不包含n的任意的因子。
    2. 用一个数a去除去另一个数b,会得到a关于b的余数,可按余数为0和余数为非0将数a分成两类。

    比如120中3的倍数有40个(余数为0),非3的倍数有80个(余数为1和2)。

    (phi(120)=120 imes(1-frac{1}{2}) imes(1-frac{1}{3}) imes(1-frac{1}{5})=32)

    所以整个式子的意思就是先在120里取非2倍数的数,再在这部分里面取非3倍数的数,然后再在这倍数里面取非5倍数的数。(乘法原理)

    欧拉函数的代码实现

    单求一个数字n的欧拉函数——分解质因数算法

    首先,我们需要将给定的n给分解成质因子相乘。

    这个时候就需要用到试除法来分解质因数

    	    for(int i=2;i*i<=n;i++)
    	    	if(n%i==0)
    	    	{
    	    		pri.push_back(i);
    	    		while(n%i==0)
    	    		    n/=i;
    	        }
    	     if(n>1) pri.push_back(n);
    

    这里for中i*i<=n的循环条件必然会使得最终剩下有且只有一个没有除干净的数字(可用反证法证明,假设有终止的时候n仍可以用两个不同的因子来组成,那么循坏的i是卡在较小的那个数字上,而较小的数字乘上它本身是固然会小于这个较小的数字去乘上一个较大的数字的,而这时也就意味着循环没有被终止,这个较小的因子最终也会被除去)或者只剩下1。

    题目AcWing 873. 欧拉函数

    image

    #include <bits/stdc++.h>
    #define MEM(a,x) memset(a,x,sizeof(a))
    #define W(a) while(a)
    #define gcd(a,b) __gcd(a,b)
    #define pi acos(-1.0)
    #define PII pair<int,int>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define ll long long
    #define ull unsigned long long
    #define rep(i,x,n) for(int i=x;i<n;i++)
    #define repd(i,x,n) for(int i=x;i<=n;i++)
    #define MAX 1000005
    #define MOD 1000000007
    #define INF 0x3f3f3f3f
    #define lowbit(x) (x&-x)
    using namespace std;
    int t,n;
    vector<int > pri;
    int main()
    {
    	cin>>t;
    	while(t--)
    	{
    	    pri.clear();
    		
    	    cin>>n;
    	    int ans=n;	
    	    for(int i=2;i*i<=n;i++)
    	    	if(n%i==0)
    	    	{
    	    		pri.push_back(i);
    	    		while(n%i==0)
    	    		    n/=i;
    		}
    		if(n>1) pri.push_back(n);
    		
    		for(int i=0;i<pri.size();i++)
    			ans=ans/pri[i]*(pri[i]-1);
    			
    		cout<<ans<<endl;
    	} 
        return 0;
    }
    
    

    求1到n中所有数字的欧拉函数和——筛法——欧拉筛

    前置

    根据算术基本定理,我们可以知道任意的一个数n,有(n=p_{1}^{k_1} imes p_{2}^{k_2} imes p_{3}^{k_3} imes... imes p_{n}^{k_n}),其中(p_{i})表示质数。

    由于我们前面的算法的实际情况和原理,质因子所搭配的指数(k_i)对我们的答案并没有起到作用

    1. 若n为质数,(phi(n)=n-1)
    2. 如果一个数n可以用一个质数p和未知性质的数i相乘来表示的话,分两种情况,即有(n=i imes p)
      1. i%p==0成立,说明说x包含p这个质因子,而这时我们要计算的时(phi(i imes p)),那么借由上文所提及到质因子所搭配的指数(k_i)对我们的答案并没有起到作用的理论基础,我们可以简单将(phi(i))理解成是一个周期,而去把p理解成周期数,因而有,(phi(i imes p)=phi(i) imes p)
      2. i%p==0不成立,则说明x是不包含p这个质因子,也就是在求(i imes p)的欧拉函数时,是有(frac{p-1}{p})份符合要求的和(frac{1}{p})不符合要求的,而换个角度和基于已经算好(phi(i))的基础上,我们就有(phi(i imes p)=phi(i) imes (p-1)),这个时候我们可以简单把(phi(i))理解成是一个周期数,而(p-1)是单个周期的量。
      3. 关于(phi(i))还是(phi(p)),要采取的是(phi(i)),因为i和所求的数仅仅差一个质数p,而p和所求的数差了一个相对未知的i,后者的做法很容易造成信息的丢失。

    题目AcWing 874.筛法求欧拉函数

    image

    #include <bits/stdc++.h>
    #define MEM(a,x) memset(a,x,sizeof(a))
    #define W(a) while(a)
    #define gcd(a,b) __gcd(a,b)
    #define pi acos(-1.0)
    #define PII pair<int,int>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define ll long long
    #define ull unsigned long long
    #define rep(i,x,n) for(int i=x;i<n;i++)
    #define repd(i,x,n) for(int i=x;i<=n;i++)
    #define MAX 1000005
    #define MOD 1000000007
    #define INF 0x3f3f3f3f
    #define lowbit(x) (x&-x)
    using namespace std;
    const int N = 1e6+10;
    int cntprime,phi[N],nprime[N],prime[N];
    bool used[N];
    int Euler(int n)
    {
    	phi[1]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if(!nprime[i])
    		   prime[cntprime++]=nprime[i]=i,phi[i]=i-1;
    		for(int j=0;j<cntprime;j++)
    		{
    			if(prime[j]*i>n)
    			   break;
    			if(i%prime[j]==0)
    			{
    				phi[i*prime[j]]=phi[i]*prime[j];
    				nprime[i*prime[j]]=prime[j];
    		        break;
    				//因为i已经包括了prime[j],那么i*prime[j+1]必然能被prime[i]整除,
    				//而就没有必要再往上面跑了
    				//这里这个break保证O(n)的算法复杂度 
    			}
    			else 
    			{
    				nprime[i*prime[j]]=prime[j];
    				phi[i*prime[j]]=phi[i]*(prime[j]-1);
    			}
    		}	
    	}
    }
    int main()
    {
    	int n;
    	cin>>n;
    	Euler(n);
    	
    	ll ans=0;
    	for(int i=1;i<=n;i++) ans+=phi[i];
    	cout<<ans;
        return 0;
    }
    
    
  • 相关阅读:
    css03层次选择器
    css02基本选择器
    Distance Between Points
    CloseHandle(IntPtr handle)抛异常
    关于win7上内存占用较大的说明
    C# WPF 显示图片和视频显示 EmuguCv、AForge.Net测试(续)
    五种开源协议的比较(BSD_Apache_GPL_LGPL_MIT)
    C# WPF 显示图片和视频显示 EmuguCv、AForge.Net测试
    Opencv不用每次创建项目配置vs2010 vc++目录 库目录等项
    矩阵运算
  • 原文地址:https://www.cnblogs.com/BeautifulWater/p/15248662.html
Copyright © 2011-2022 走看看