zoukankan      html  css  js  c++  java
  • 数论专题学习笔记

    写在前面

    数论分为三个部分

    1.质数

    2.约数

    3.同余

     题目完成进度

    3/5


     

    一、质数

    嘛质数的定义什么的应该算是小学数学内容啦?所以这里就不讲了,提一个结论就是:自然数中的质数不多,对于一个足够大的自然数N,不大于N的质数大约有lnN个。

    判定单个质数方法

    1.试除法

    这是最简单的方法了,就是用这个数n去除以2~,除得尽就是合数,不然就是质数。

    2.Miller-Rabin算法

    这是一种随机的高效算法

    这里要用到一个定理——费马小定理

    若p是质数,则对于任意整数x,有xp≡x(mod p)

    若x与p互质,则有xp-1≡1(mod p)

    由此可得,对于任意质数p,以及对于模p的剩余类环{1,2,3……,p-1}中任意的x,都满足xp≡x(mod p),即xp-1≡1(mod p),这样我们就可以排除大量的合数。

    但是费马小定理中p是质数的条件只是充分条件,而非必要条件,也就是说,可能存在一个合数p,当x取到某一值时,xp≡x(mod p)也成立。

    为了提高答案的正确率,我们要选取多个不同的x来进行判断,如果有一个x无法满足条件,那么p就一定不是质数。

    但是依然存在特殊情况,有这样一类合数p,对于1~p-1的任意一个x,都满足xp≡x(mod p),那么这类合数称为Carmichael(卡曼克尔)数,比如561

    因为这类数的存在,我们利用费马小定理无法准确判断一个数是合数还是质数。

    于是我们要进行二次探测

    已知x²≡1(mod p),若p为质数,则有x≡1(mod p)或x≡p-1(mod p)

    证明先咕着

    由此我们就得到了Miller-Rabin算法,具体过程如下:

    1.用数 x 对 p 做费马测试,即判断 x^(p-1 )模 p 的值,如果 x^(p-1 ) =1 ( mod p ),则说明通过费马测试;继续做第 2 步,否则直接下结论 p 不是素数(通常选 5 个或更多已知的质数作为 x )。
    2.若 p-1 是偶数,那么可以继续通过 x^((p-1)/2) 是否等于 1 或 (p-1) 来进行测试。

    <1>如果测试后即不等于 1 也不等于 (p-1) ,则说明是合数,结束,否则继续。

    <2>如果测试 x^((p-1)/2)=1 ( mod p )通过 , 则继续执行第 2 步,即看( p-1 ) /2 是否偶数,是的话,继续递归测试,只要有一环判断不通过,那么p就是合数;如果(p-1)/2是奇数,那么就直接判定p是合数。

    <3>如果测试 x^((p-1)/2)=p-1 ( mod p )通过 ,说明到目前为止,没有条件能判定p不是质数,则此时默认p是质数。

    代码也先咕着

    如何求出1~n之间所有的质数

    1.埃氏筛法

    思想:对于任意一个质数x,2x,3x,4x……都不是质数

    具体操作:从2开始,每次扫描到一个质数,就把它的倍数标记为合数,如此推进,当扫描到一个数没有被标记时,则意味着这个数p不能被2~p-1之内的数整除,所以p一定是质数。

    改进:对于一个数x,小于x²的x的倍数已经在扫描比x小的数时被标记了,所以扫描到数x时,只需要标记比x²大的x的倍数即可。

    void prime(int n){//求出1~n之间的所有质数
        memset(v,0,sizeof(v));//1为合数,0为质数
        for(int i=2;i<=n;i++){
            if(v[i]==1) continue;//如果i是合数就不用管
            cout<<i<<endl;//输出质数
            for(int j=i;j<=n/i;j++) v[i*j]=1;//标记质数i的倍数
        }
        return;
    }

    这样做的时间复杂度为O(nlog(logn))

    改进后的埃氏筛法任然会出现重复标记素数的情况,所以就有了接下来的线性筛法

    2.线性筛法

    思路:每个合数都用它的最小质因子把它筛掉。

    void prim(int n){
        int v[N],num=0;
    memset(v,0,sizeof(v)); int prim[N]; go(i,2,n){ if(!v[i])v[i]=i,prim[++num]=i; go(j,1,num){ if(i*prim[j]>n||prim[j]>v[i])break; v[i*prim[j]]=prim[j]; } } return; }

    分解质因数

    首先是一个必须要知道的东西——算数基本定理

    任何一个大于一的整数都能唯一分解成有限个质数的乘积,可写作:N=p1c1p2c3……pmcm,其中ci都是正整数,pi都是质数,且满足p1<p2<……<pm

    放个代码就溜啦QWQ

    void divide(int n){
        int p[MAX],c[MAX],m=0;
        for(int i=2;i<=sqrt(n);i++){
            if(n%i==0){//此处i一定是质数
                p[++m]=i;c[m]=1;
                n/=i;
                while(n%i==0){//把n中的i全部除尽
                    c[m]++;
                    n/=i;
                }
            }
        }
        if(n>1) p[++m]=n;c[m]=1;//n本身可能是质数
        return;
    }

    来一道例题吧——

    Prime Distance (poj2689)

    go back


     

    二、约数

     定义也很简单,所以就不讲了,先讲一下算数基本定理的推论

    N的正约数集合可以写作{p1b1p2b2……pmbm},其中0≤bi≤ci

    N的正约数个数为:(c1+1)*(c2+1)*……*(cm+1)=$prod_{i=1}^{m} c_{i}+1$

    N的所有正约数的和为:(1+p1+p12+……p1c1)*……*(1+pm+pm2+……+pmcm)=$prod_{i=1}^{m} (sum_{j=1}^{c_{i}}(p_{i})^{j})$

    约数的求法

    1.试除法

    因为约数是成对出现的,所以只要扫描1~$sqrt{n}$的每一个数判断即可

    这样做的时间复杂度是O($sqrt{n}$)

    由此还可以得到一个推论:一个整数n的约数上限是$sqrt{n}$

    2.倍除法

    思想:对于每个数d,1~n中以d为约数的就是d的倍数。

    走一波代码——

    vector <int> factor[MAX];//factor[i][j]表示数i的第j个约数
    for(int i=1;i<=n;i++)
        for(int j=1;j<n/i;j++)
            factor[i*j].push_back(i);

    其实……挺简单的哈QAQ

    这个方法的时间复杂度为O(n/1+n/2+n/3+……+n/n)=O(nlogn)

    于是我们又可以得到一个推论:1~n的每个数的约数总和约为nlogn

    最大公约数

    有一个简单的定理:对于任意自然数a,b都有:a*b=gcd(a,b)*lcm(a,b)

    证明一下:

    设d=gcd(a,b),a0=a/d,b0=b/d。

    根据最大公约数的定义,可得gcd(a0,b0)=1;又由最小公倍数的定义可得,lcm(a0,b0)=a0*b0.

    于是lcm(a,b)*gcd(a,b)=lcm(a0*d,b0*d)*d=lcm(a0,b0)*d*d=(a0*d)*(b0*d)=a*b

    好这个东西后面不会讲到它了但是做题很有用所以一定要记住

    嘛讲一下求最大公约数的方法吧

    1.更相减损术

    对于任意自然数a,b,且a≥b,都有gcd(a,b)=gcd(b,a-b)=gcd(a,a-b)

    对于任意自然数a,b,有gcd(2a,2b)=2*gcd(a,b)

    2.欧几里得算法

    对于任意自然数a,b,且b≠0,有:gcd(a,b)=gcd(a,a%b)

    证明

    若a<b,则gcd(b,a%b)=gcd(b,a)=gcd(a,b)

    若a≥b,设a=q*b+r,其中0≤r<b。显然r=a%b。对于a,b的任意公约数d,因为d|a,d|(q*b),故d|(a-q*b),即d|r

    代码好短呀就一行

    int gcd(int a,int b) {return b?gcd(b,a%b):a;}

    欧几里得算法求最大公约数的复杂度为min(O(loga),O(logb)),即O(logn)

    互质与欧拉函数

    互质的概念就不讲了,说一说欧拉函数的概念吧:[1,N]中与N互质的数的个数被称为欧拉函数,记为φ(N)

    然后是几条非常重要的关于欧拉函数的性质

    1.φ(1)=1

    没错1与自己互质的,因为gcd(1,1)=1符合互质的定义

    2.当p为质数时,φ(p)=p-1

    因为质数的因数只有1和自己本身,所以在[1,p]的范围内和质数p互质的数就是出了p本身之外的p-1个数

    3.当p为质数时,φ(pk)=pk-pk-1=pk(1-1/p)

    哗我不记得证明过程了,还是先咕着

    4.对于任意的n>1,[1,n]中与n互质的数的和为n*φ(n)/2

     

    5.由算数基本定理可得,φ(n)=n*$prod_{质数p|n}(1-1/p)$,这也是欧拉函数的计算公式

     设p是n的质因子,则1~n中p的倍数有p,2p,……(n/p)*p,共n/p个。同理,q作为n的质因子,则1~n中q的倍数有n/q个。如果我们把这n/p+n/q个数去掉,那么p*q的倍数被排除了两次,需要加回来一次。因此,1~n中不与n含有共同质因子p或q的数的个数为:$n-frac{n}{p}-frac{n}{q}-frac{n}{p*q}=n*(1-frac{1}{p}-frac{1}{q}-frac{1}{p*q})=n(1-frac{1}{p})(1-frac{1}{q})$

    6.若a,b互质,则有φ(a*b)=φ(a)*φ(b),即欧拉函数为积性函数

     

    7.一个数n,等于它的约数的欧拉函数值之和,即n=$sum_{d|n}d$

     

    8.若p|n且p2|n,则有φ(n)=φ(n/p)*p

    因为p|n且p2|n,所以n和n/p包含相同的质因子,按照计算公式算出φ(n)和φ(n/p)的值相除得商为p,得证

    9.若质数p|n且p2$ otmid$n,则φ(n)=φ(n/p)*(p-1)

    因为p|n且p2$ otmid$n,所以p与n/p互质,由性质6可得φ(n)=φ(n/p)*φ(p),而p是质数,所以可得φ(p)=p-1

    综上,φ(n)=φ(n/p)*(p-1),得证

    那么又要如何计算每个数的欧拉函数值呢?其实可以在分解质因子的过程中一并完成,详见代码

    int Oula(int n){//求phi[n]的值
        int ans=n;
        for(int i=2;i*i<=n;i++){//分解质因子
            if(n%i==0){
                ans=ans/i*(i-1);//性质5的公式变形,计算欧拉函数
                while(n%i==0) n/=i;
            }
        }
        if(n>1) ans=ans/n*(n-1);
        return ans;
    }

    这样的算法时间复杂度为O($sqrt{n}$)

    这里也放一道例题吧QWQ

    Luogu P2158 仪仗队

    go back


    三、同余

    同余

    1.同余

    同余,顾名思义就是余数相同的意思

    若整数a和整数b除以正整数m的余数相等,则称a,b模m同余,记为a≡b(mod m)

    2.同余类

    对于$forall ain[0,m-1]$,集合{a+km}($kin Z$)中的所有数在模m的意义下与a同余,余数都是a,则该集合称为一个模m的同余类,记为$overline{a}$

    3.剩余系

    模m的同余类共有m个,分别是$overline{0},overline{1},overline{2},……,overline{m-1}$,它们构成m的完全剩余系

    1~m中与m互质的数代表的同余类有φ(m)个,它们构成m的简化剩余系

    m的简化剩余系关于模m乘法封闭

    差不多就这样,不过这里还有一个比较重要的东西就是欧拉定理

    若正整数a,n互质,则有aφ(n)≡1(mod n)

    同时还有一个推论

    若正整数a,n互质,则对于任意的正整数b,都有ab≡ab%φ(n)(mod n)

    这里放一个例题——(我还没有做完的例题QAQ)

    The Luckiest Number(poj3696)

    扩展欧几里得

    1.贝祖定理

    对于任意整数a,b,存在一对整数x,y,满足ax+by=gcd(a,b)

    证明

    在算法的最后一步,即b=0时,显然可得x=1,y=0

    若b>0,则gcd(a,b)=gcd(b,a%b)。假设此时存在一对整数x,y满足b*x+(a%b)*y=gcd(b,a%b),因为bx+(a%b)y=bx+(a-b*$lfloor a/b floor$)y=ay-b(x-$lfloor a/b floor$*y),所以我们可以令x1=y,y1=x-$lfloor a/b floor$*y,这样就得到了ax1+by1=gcd(a,b)

    2.代码实现

    int exgcd(int a,int b,int &x,int &y){//返回值为gcd(a,b)
        if(b==0) {x=1;y=0;return a;}//如果b=0,就到了最后一步
        int d=exgcd(b,a%b,x,y);//递归调用
        int z=x;x=y;y=z-y*(a/b);
        return d;
    }

    3.扩展延伸

    对于更加一般的方程ax+by=c,它有解当且仅当gcd(a,b)|c,我们可以先求出ax+by=gcd(a,b)的一组特解x0,y0,然后同时乘上c/gcd(a,b)

    事实上,方程ax+by=c的通解可以表示为:$x=frac{c}{gcd(a,b)}*x_{0}+k*frac{b}{gcd(a,b)}$,$y=frac{c}{gcd(a,b)}*y_{0}-k*frac{a}{gcd(a,b)}$  ($kin Z$)

    乘法逆元

    若整数b,m互质,并且b|a,则存在一个整数x,使得a/b≡a*x(mod m),称x为b的模m乘法逆元,记为b-1(mod m)

    这里我不想写了,去看一下wch的blog吧QAQ

    放一个例题,这是第四个例题了——

    Sumdiv(poj1845)

    线性同余方程

    给定整数a,b,m,求一个整数x满足a*x≡b(mod m),或者给出无解。因为未知数的指数为1,所以我们称之为一次同余方程,也称线性同余方程

    a*x≡b(mod m)等价于a*x-b是m的倍数,不妨设为-y倍。于是,该方程可以改写为a*x+m*y=b,于是这就和上面提到的方程十分类似了,所以可得x=x0*b/gcd(a,m)就是原线性同余方程的一个解。

    方程的通解则是所有模(m/gcd(a,m))与x同余的整数

    第五道例题——

    同余方程(NOIP2012)

    这里再提一个东西叫做中国剩余定理,先放一放,把前面的落实完了再来学这个

    go back

  • 相关阅读:
    文件批量上传
    XML如何不进行解析原样输出
    1456710017
    java标准百分比
    解决SSM项目下静态资源(img、js、css)无法引用的问题
    MySQL查询当天数据以及大量查询时提升速度
    多线程test
    JAVA中等待所有线程都执行结束(转2)
    java中等待所有线程都执行结束
    关于Java多线程(JAVA多线程实现的四种方式)
  • 原文地址:https://www.cnblogs.com/THWZF/p/10457243.html
Copyright © 2011-2022 走看看