菜鸡学数论(一)

先确认一下入门数论的基本知识点:

  1. 最大公约数

  2. 快速幂

  3. 素数筛


辗转相除求最大公约数:

int gcd(int a,int b)
{
    if(b==0) return a;
    gcd(b,a%b);
}

素数筛的模板(线性筛):将2到n之间的整数记录下来,其中最小的素数是2,将表中2的倍数全部不划去,表中剩余的最小的数字为3,再将3的倍数全划去,以此类推,就能找到所有的素数。这样做的时间复杂度只有o(nlog log n)。

int prime[maxn];///用于标记晒是否已经筛过
int pri[maxn];///用于储存素数
void pre_prime()
{
    memset(prime,0,sizeof(prime));
    cnt = 0;
    prime[0] = prime[1] = 1;
    for(int i = 2 ; i < maxn  ; i++)
    {
        if(!prime[i]) pri[cnt++] = i;
        for(int j = 0 ; j < cnt && i * pri[j] <= maxn ; j++)
        {
            prime[i * pri[j]] = 1;
            if(i % pri[j] == 0) break;///已经筛过的不要重复筛
        }
    }
}

快速幂:数论问题经常遇见非常大的数,例如 X ^ 22,通常的思路是采用for循环,但当数很大时运算效率会很低。

采用快速幂算法可以将X22拆为X16 , X4和X2。时间复杂度直降至o(log n)。

    typedef long long ll;
    ll quick_pow(ll x,ll n,ll mod)
    {
        ll ans=1;
        while(n>0)
        {
            if(n&1) 
                ans=ans*x%mod;
            x=x*x%mod;
            n>>=1;
        }
        return ans;
    }

敲黑板:n&1的作用是判断最后一位是否是1,是一的话就是要单乘一次再翻倍(因为有右移n>>1),不是一的话直接翻倍.


欧拉函数
欧拉函数是少于或等于n的数中与n互质的数的数目。
欧拉函数的性质:它在整数n上的值等于对n进行素因子分解后,所有的素数幂上的欧拉函数之积。
欧拉函数的值  通式:φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)……(1-1/pn),其中p1, p2……pn为x的所有质因数,x是不为0的整数。φ(1)=1(唯一和1互质的数(小于等 于1)就是1本身)。 (注意:每种质因数只一个。比如12=223,那φ(12)=12*(1-1/2)*(1-1/3)=4)
推论:当n为奇数时,有φ(2n)=φ(n)。
若n是质数p的k次幂,φ(n)=pk-p(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟n互质。
设n为正整数,以 φ(n)表示不超过n且与n互素的正整数的个数,称为n的欧拉函数值,这里函数φ:N→N,n→φ(n)称为欧拉函数。
欧拉函数是积性函数——若m,n互质,φ(mn)=φ(m)φ(n)。
特殊性质:当n为奇数时,φ(2n)=φ(n), 证明与上述类似。
那么如果我们想要求出其欧拉函数的值就要先对n进行质因子分解:
如果进行直接进行筛因子的话其时间复杂度为O(n),代码如下

int prime(int n)
{
    int rea=n;
    for(int i=2; i<=n; i++)
        if(n%i==0)//第一次找到的必为素因子
        {
            rea=rea-rea/i;
            do
                n/=i;//把该素因子全部约掉
            while(n%i==0);//比如12=2*2*3这样可以将所有的2除去
        }
    return rea;
}

有没有方法将其降低复杂度呢?我们知道 “ 由于任何一个合数都至少有一个不大于根号n的素因子 ”,所以只需遍历到根号n即可

int prime(int n)
{
    int rea=n;
    for(int i=2; i*i<=n; i++)
        if(n%i==0)//第一次找到的必为素因子
        {
            rea=rea-rea/i;
            do
                n/=i;//把该素因子全部约掉
            while(n%i==0);
        }
    if(n>1)
        rea=rea-rea/n;//如果到最后n有就将n直接处理 
    return rea;
}

最后学长说用筛法求欧拉函数很好(可能我太菜了吧,,,,没有领会到)

primel prime[50000];
int pri[20000];
void prim()
{
    memset(prime,0,sizeof(prime));
    prime[0]=prime[1]=1;
    int k=0;
    for(int i=2; i<50000; i++)
    {
        if(!prime[i])
            pri [k++]=i;
        for(int j=0; j1)
        rea=rea-rea/n;
    return rea;
}

你可能感兴趣的:(基础篇,数论)