zoukankan      html  css  js  c++  java
  • 数论知识小结 [基础篇]

    数论知识小结 [基础篇]

    (latest updated on 2020.08.12)


    符号((a,b)=gcd(a,b))

    乘除(a|b ightarrow b=ka (kin N^+))

    (sum)求和,(prod)求积

    任意(forall),存在(exists)

    $lfloor x floor $ 向下取整,$lceil x ceil $ 向上取整

    ([a,b])区间,通常指整数即([a,b]cap )

    调和级数

    数学上,调和级数为(H_n=sum_{i=1}^{n}frac{1}{i})

    OI中,我们常用调和级数分析(sum_{i=1}^nfrac{n}{i}approx nln n)

    把它近似看成是(f(x)=frac{n}{x})([1,n])上的积分,它的原函数(F(x)=nln x)

    (egin{aligned} sum_{i=1}^nfrac{n}{i}approx int_1^nf(x) dx=F(n)-F(1)=nln n-napprox nln nend{aligned})

    附:广义调和级数 (egin{aligned} H^z_n=sum_{i=1}^{n}frac{1}{i^z} end{aligned})它的无穷形式为黎曼函数(egin{aligned}zeta(z)=H^{z}_{infty}end{aligned})

    [ ]

    向下取整的性质/数论分段

    性质:$lfloor frac{n}{ab} floor =lfloor frac{lfloor frac{n}{a} floor }{b} floor $

    [ ]

    数论分段:即对于(i=[1,n]),把(i)按照(lfloor frac{n}{i} floor)的值分成(O(sqrt n))段,(通常为(2cdot sqrt n)段左右)

    证明:对于(ileq sqrt n),显然只有(sqrt n)个不同的值

    对于(i>sqrt n),此时(frac{n}{i}<sqrt n),也只有(sqrt n)个不同的值

    [ ]

    素数/质数/质数密度

    对于(x>1),若( exists yin[2,n-1],y|x),则(x)是一个质数,下文称素数集合为(prime)集合

    (n)以内的素数个数用函数(pi(n)=|primecap[1,n]|)表示,素数密度在渐进意义上是(O(log n))

    即可以认为(pi(n)=O(frac{n}{log n}))(实际在(n)较小时完全看不出这一点)

    (n)的唯一分解: (n=prod_{p_iin prime} p_i^{c_i})

    朴素的素数判别法:

    一个显然方法的如果(x)不是质数,则(exists yin [2,sqrt n] ,y|x)

    所以可以朴素写出一个(O(sqrt n))的素数判别法

    利用这一点预先处理小素数,每次只判断素数,可以写出一个(O(pi(sqrt n)))的素数判别法

    朴素的质因数分解法:

    即求(n=prod p_i^{c_i},p_iin prime),其中(sum c_ileq log _2^n)

    由于对于一个数(n),最多存在一个(yin prime cap [sqrt n,n],y|n),因此可以先分解掉(y<sqrt n)的部分,剩下的一个就知道了

    void Factor(int n){
        for(int i=2;i*i<=n;++i) if(n%i==0)  
            while(n%i==0) printf("%d ",i),n/=i; // 分解掉<sqrt(n)的部分
        if(n>1) printf("%d ",n); // 如果还剩下,那么就是>sqrt(n)的一个质数
    }
    

    复杂度是(O(sqrt n +log n))

    更优化的只枚举质数作为因子,复杂度是(O(pi(sqrt n)+log n))

    [ ]

    朴素的素数筛法:埃氏筛

    对于([2,n])每个数(i)(x=ki(k>1))均不是质数,直接枚举复杂度为调和级数(O(nln n))

    更优化的,对于([2,n])中每个质数(i)(x=ki(k>1))均不是质数,由于素数密度为(O(log n)),所以可以近似认为是(O(nlog log n))(实际我不会证明。。)

    线性筛(欧拉筛)

    筛素数知识一个基础,可以筛很多函数,尤其是适用于积性函数

    直接上代码背板子好了

    int notpri[N],prime[N],primecnt;
    void Sieve(){
        notpri[1]=1;
        for(int i=2;i<=n;++i) {
            if(!notpri[i]) prime[++primecnt]=i;
            for(int j=1; j<=primecnt && 1ll*i*prime[j]<=n;++j){
                notpri[i*prime[j]]=1;
                if(i%prime[j]==0) break;
            }
        }
    }
    
    

    其中(imod prime_j= 0)意味着后面的数已经被(frac{i}{prime_j})筛掉了,所以可以break

    复杂度是带有一定常数的(O(n)),还可以用来筛其他的一些函数

    [ ]

    [ ]

    (gcd, ext{lcm})

    最大公因数( ext{gcd(greatest common divisor)})常用(gcd(x,y)=(x,y))表示

    最小公倍数( ext{lcm(least common multiple)})

    特别的:((a,0)=a)

    (gcd(a,b))可以用辗转相除法(也称为欧几里得算法),即利用性质((a,b)=(amod b,b))

    每次递归调用(gcd(b,amod b))即可,边界为((a,0)=a)

    复杂度分析:

    首先是取模的分析,对于(forall a,b,age b),必然满足(amod b leq frac{a}{2})

    所以每次取模至少会减少一倍,复杂度为(O(log a))

    [ ]

    附:扩展欧几里得算法

    用于求解不定方程(ax+by =1)的一组解((x,y))

    存在解的条件是((a,b)=1),否则((a,b)|ax+by),即(ax+by>1)

    用类似求( ext{gcd})的方法求出

    (ax+by=1)

    (( amod b)cdot x+ lfloor frac{a}{b} floorcdot bcdot x + by=1)

    (( amod b)cdot x+ bcdot (lfloor frac{a}{b} floorcdot x + y)=1)

    那么如果求出((amod b)cdot x'+bcdot y'=1)的解

    (x'=x,y'=lfloor frac{a}{b} floor cdot x+y)

    (x=x',y=y'-lfloor frac{a}{b} floor x)

    每次都递归求出$(amod b) cdot x+ by =1 $

    复杂度与(gcd)相同,最后的((a,b)=a=1,b=0)是边界条件,此时(x+0cdot y=1)的一组解是((1,0))

    写成代码

    // ax + by =1
    int Exgcd(int a,int b,int &x,int &y) {
        if(b==0){ 
            if(a!=1) return 0; // (a,b)!=1 ,不存在解
            x=1,y=0; // 初始解
            return 1;
        } 
        Exgcd(b,a%b,y,x),y-=a/b*x; // 带入上面的式子,注意这里带入的是(b,a%b),所以x,y要反过来
        return 1;
    }
    

    注意求出的(x,y)不保证为正数

    [ ]

    [ ]

    [ ]

    因数个数

    似乎没有找到规范的函数定义,所以下称(d(n))

    (d(n)=|{i|iin[1,n]and i|n}|)

    对于(n=prod p_i^{c_i}),列一条小学的公式(d(n)=prod (c_i+1))

    因数个数一个非常松的上界是(O(sqrt n)),证明这里略去

    实际上,搜索得到的上界大致是

    maxn=10^ 1  max= 4
    maxn=10^ 2  max= 12
    maxn=10^ 3  max= 32
    maxn=10^ 4  max= 64
    maxn=10^ 5  max= 128
    maxn=10^ 6  max= 240
    maxn=10^ 7  max= 448
    maxn=10^ 8  max= 768
    maxn=10^ 9  max= 1344
    maxn=10^10  max= 2304
    maxn=10^11  max= 4032
    maxn=10^12  max= 6720
    maxn=10^13  max= 10752
    maxn=10^14  max= 17280
    maxn=10^15  max= 26880
    maxn=10^16  max= 41472
    maxn=10^17  max= 64512
    maxn=10^18  max= 103680
    

    可以看到,因数个数是非常少的

    附:

    质因数个数(Omega(n)=|primecap [1,n]|),非常松的上界是(Omega(n)=O(log n))

    约数和函数(sigma(n)=sum_{i|n}i)

    [ ]

    费马小定理/欧拉定理/欧拉函数/阶

    [ ]

    费马小定理: 对于任意质数(P,x>0,x^{p-1}equiv 1 pmod P)

    [ ]

    欧拉函数:(varphi(n))([1,n-1])中与(n)互质的数个数,特别的(varphi(1)=1)

    (n=prod p_i^{c_i})其中(p_i)是质数,(c_i>0),则(varphi(n)=prod p_i^{c_i-1}(p_i-1)=nprodfrac{p_i-1}{p_i})

    对于可以通过类似筛素数的方法求出来([1,n])(varphi(i))

    对于数(n),需要采用质因数分解求出,朴素的做法为(O(sqrt n)),用( ext{Pollard's Rho})算法复杂度更低

    [ ]

    欧拉定理:对于任意数(P>1,x>0,x^{varphi(P)}equiv 1pmod P)

    推论:(x^cequiv x^{cmod varphi (P)} pmod P)

    很显然,费马小定理是欧拉定理的特殊情况

    [ ]

    阶:对于((a,n)=1)的整数,满足(a^r≡1 pmod n) 的最小整数(r),称为(a)(n)的阶,以下简称(d(a))

    显然(a^i mod n)构成了一个以(r)为最小正周期的循环

    性质:根据欧拉定理(a^{varphi(n)}mod n=1),所以有(d(a)|varphi(n))

    求解阶:先对于 (varphi(n))质因数分解,然后可以

    1.依次枚举每个因数判断是否有(a^imod n=1),取最小的(i),复杂度为(O(sqrt nlog n))

    2.设(varphi(n)=p_i^{c_i}),令(x=varphi(P)),从(x)开始,如果(a^{frac{x}{p_i}}mod n=1),则(x ightarrow frac{x}{p_i})

    预处理复杂度受限于质因数分解(下文有介绍)

    单次查询复杂度上限是(O(log ^2 P))(为快速幂复杂度乘上(sum c_i))

    [ ]

    模逆元

    对于任意数(x>1,P>1,(x,P)=1),存在一个数(frac{1}{x}equiv ypmod P)

    (xyequiv 1 pmod P)(y)(x)的一个模逆元

    (P)为质数时,由于(x^{P-1}equiv 1 pmod P),所以(yequiv x^{P-2}pmod P)(x)的一个逆元

    (P)不为质数时,如果已知(varphi (P)),可以类似得做,否则可以构造(acdot x+bcdot P=1),用扩展欧几里得算法求出一组合法解((a,b)),则(a)即为一个答案

    [ ]

    原根/指标

    原根:一个数(P)有原根的条件是他可以表示为(P=1,2,4,p,2p,p^n(pin prime))

    对于(P),令(d(x))(x)(P)的原根,若存在(d(x)=varphi(P)),则(x)(P)的一个原根

    找原根:

    (varphi(P)=prod p_i^{c_i}),其中(p_i)是质数

    由于(d(x)|varphi(P)),如果(d(x)<varphi(P))那么必然存在一个(x^{frac{varphi(P)}{p_i}}equiv 1pmod P)

    所以先求一遍质因数分解,然后快速幂判断就可以做到(O(log ^2 P))判断原根

    显然原根不唯一

    已经被证明对于任意的(P)如果存在原根,则其最小原根最多是(O(P^{frac{1}{4}}))级别的

    [ ]

    指标:

    对于一个数(P)和它的一个原根(x),对于(gcd(y,P)=1),则(y)一定可以用(x^i)表示,那么(i)就是(y)的指标

    同时,(y)(P)的阶就是(d(y)=frac{varphi(P)}{gcd(varphi(P),i)}),可以认为是数列(a_j=icdot jmod varphi(P))的周期问题

    可以使用( ext{BSGS})算法在(O(sqrt Plog P))或者(O(sqrt P))的时间内求出一个数的指标

    (关于去掉(log P):只需要处理出模逆元直接累乘,每次用( ext{Hash Table})访问即可)

    指标在二次剩余的较劣做法中也有应用,同时也可以直接套用性质用于阶的求解

    [ ]

    快速乘

    (x,yin[0,P-1],Pleq 10^{18},xcdot y mod P)

    直接乘法会爆long long

    可以用类似快速幂的方法写,复杂度为(O(log P))

    typedef long long ll;
    typedef unsigned long long ll;
    void qmul(ll x,ll y,ll P){
        ll res=0;
        for(;y;y>>=1,x=(x+x)%P) if(y&1) res=(res+x)%P;
        return res;
    }
    

    另一种方法是强行用long double 保证精度,然后计算的时候用unsigned long long 溢出回来,(O(1))但是很奇怪,通常不会挂

    typedef long long ll;
    typedef unsigned long long ll;
    void qmul(ll x,ll y,ll P){
        ull z=(long double)x/P*y;
        ll res=(ull)x*y-(ull)z*P;
        return (res%P+P)%P;
    }
    

    [ ]

  • 相关阅读:
    Python学习记录——Ubuntu(四)计划任务、grep、正则表达式、sed、awk
    Python学习记录——Ubuntu(三)文件操作
    Python学习记录——Ubuntu(二)用户和用户组、环境变量
    Python学习记录——Ubuntu(一)基本配置、快捷键和系统启停命令行
    【转】Pycharm常用快捷键
    Python学习记录——文件操作
    闭包详解
    关于django中前端表单提交那点事
    Django+celery+redis 异步发送邮件功能
    Nginx
  • 原文地址:https://www.cnblogs.com/chasedeath/p/13092822.html
Copyright © 2011-2022 走看看