zoukankan      html  css  js  c++  java
  • 数论

    取模运算

    同余

    设a,b是整数,若它们除以正整数m的余数相同,则称a,b对于模m同余,记为aΞb(mod m)。

    费马小定理

    假如p是质数,a是整数,且a、p互质,那么a的(p-1)次方除以p的余数恒等于1,即:a^(p-1)≡1(mod p)  可以改写为bbc-2Ξ1(mod c)

    但是反过来却不一定成立,就是说,如果a、p互质,且a^(p-1)≡1(mod p),不能推出p是质数,比如Carmichael数。

    加法和乘法形式相同

    (a + b)%c = (a%c + b%c)%c

    (ab)%c = (a%c)(b%c)%c

    注意减法和除法的取模运算

    (a − b)%c = (a%c − b%c + c)%c

    (a / b)%c = ab-1%c

    最大公因数和最小公倍数

    记a,b的最大公因数为gcd(a,b)

    最大公因数的性质

    1. gcd(a,b)=gcd(b,a)
    2. gcd(a,b) = gcd(a − b,b)(a ≥ b)
    3. gcd(a,b) = gcd(a%b,b)
    4. gcd(a,b,c) = gcd(gcd(a,b),c)

    性质2即为辗转相减法

    性质3即为欧几里得算法

    性质4可以说明多个数的最大公因数可以递推地进行求解

    性质3求最大公因数的代码

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

    最小公倍数lcm

    int lcm(int a,int b)
    {
        return a*b/gcd(a,b);
    }

    扩展欧几里得算法

    考虑方程      ax+by=c      其中a,b,c是已知的正整数,如何求出方程的解

    定理 上述方程有解的充要条件是 gcd(a,b)|c(cgcd(a,b)的倍数)

    我们先将问题简化,求方程  ax+by=d 的解,其中 a,b是正整数, d=gcd(a,b)

    算法过程

    求方程   36x+28y=4  的解

    1. 36=28×1+8
    2. 28=8×3+4
    3. 8=4×2

    将前两步改写一下:

    1. 8=36−28×1
    2. 4=28−8×3

    再代入一下,得
    4=28−(36−28×1)×3=28×4+36×(−3)
    因此

    x=−3,y=4 是方程的一组解,方程的任一解可以表示成

      x=−3+7t
      y=4−9t

    更一般地,方程 ax+by=d,d=gcd(a,b)的所有解为

        其中 x0,y0是一组特解

    回到最开始的问题,方程 ax+by=c,gcd(a,b)|c的所有解为

    其中 x0,y0是方程 ax+by=d,d=gcd(a,b)的一组特解.

    ll ext_gcd(ll a,ll b,ll& x,ll& y)
    {
        ll d = a;
        if (!b){
            x = 1;y = 0;
        }else{
            d = ext_gcd(b,a%b,y,x);
            y -= a/b*x;
        }
        return d;
    }

    回到前面除法取模中挖的坑,要找到 b-1使得 bb-1≡1(mod c),实质上是求解方程 bx+cy=1中的 x,当然只有 gcd(b,c)=1时才有解,否则逆元不存在

    素数

    素数定义:如果一个数只能被1和它本身整除那么我们就成其为素数。

    一般的素数判断

    bool isPrime(int n)
    {
        if(n==1)
        return false;
        for(int i=2;i*i<=n;i++)
            if(n%i==0)
            return false;
            return true;
    }

    素数筛

    埃拉托斯特尼筛法,或者叫埃氏筛法

    const int N = 100005;  
    bool prime[N];  
    void init()
    {  
        for(int i=2;i<N;i++) 
        prime[i]=true;//先全部初始化为素数  
            for(int i=2;i<N;i++)
            {  
                if(prime[i])//如果i是素数
                {  
                     for(int j=i*i;j<N;j+=i)//从i的两倍开始的所有的倍数(i*i也行)
                     {  
                           prime[j] = false;  
                     }  
                }  
            }  
    }

    这种方法的原理是从小到大将素数的倍数筛掉,复杂度为 O(n*logn),注意到每个合数如果有多个素因子,那么就 会被重复筛掉,造成复杂度的浪费,因此,用下面的方法可以保证每个合数只被它最小的素因子筛掉一遍,以O(n)的复杂度解决上述问题

    线性筛法(欧拉筛)

    ll getPrime(ll n,bool vis[],ll prime[]){
        ll tot=0;
        for(ll i=1;i<=n;i++)vis[i]=0;
        for(ll i=2;i<=n;i++){
            if(!vis[i])prime[tot++]=i;
            for(ll j=0;j<tot;j++){
                if(prime[j]*i>n)break;
                vis[prime[j]*i]=1;
                if(i%prime[j]==0)break;
            }
        }
        return tot;
    }

     Miller_Rabin大素数判定

      bool miller_rabin(long long n)//Miller-Rabin素数检测算法  
      {      
          long long i,j,a,x,y,t,u,s=10;      
          if(n==2)        
            return true;      
          if(n<2||!(n&1))        
            return false;      
          for(t=0,u=n-1;!(u&1);t++,u>>=1);//n-1=u*2^t      
          for(i=0;i<s;i++)      
          {          
             a=rand()%(n-1)+1;          
             x=ModExp(a,u,n);          
             for(j=0;j<t;j++)          
             {              
                 y=mood(x,x,n); //对x的x次方快速幂然后对n取模             
                 if(y==1&&x!=1&&x!=n-1)                
                 return false;              
                 x=y;          
             }          
             if(x!=1)            
             return false;     
          }      
             return true;
       }
                

     预处理每个数的所有质因数

    const int N = 100000 + 5;  
    vector<int > prime_factor[N];  
    void init()
    {  
        for(int i = 2; i < N; i ++)
            if(prime_factor[i].size() == 0)//如果i是质数
                for(int j = i; j < N; j += i)
                    prime_factor[j].push_back(i);   
    }

    预处理每个数的所有因数

    const int N = 100000 + 5;  
    vector<int > factor[N];  
    void init()
    {  
        for(int i = 2; i < N; i ++)
            for(int j = i; j < N; j += i)
                factor[j].push_back(i);
    }

    预处理每个数的质因数分解

    const int N = 100000 + 5;  
    vector<int > prime_factor[N];  
    void init()
    {  
        int temp;  
        for(int i = 2; i < N; i ++)
            {  
            if(prime_factor[i].size() == 0)
            {  
                for(int j = i; j < N; j += i)
                {  
                    temp = j;  
                    while(temp % i == 0)
                    {  
                        prime_factor[j].push_back(i);  
                        temp /= i;  
                    }  
                }  
            }  
        }  
    }

    组合数运算和快速幂见另一篇博客https://www.cnblogs.com/zlhdbk/p/10548959.html

  • 相关阅读:
    阿里云乌班图16配置-PHP环境(包括mysql及apache安装)
    mysql主从复制跳过错误
    64位系统下powerdesigner15连接oracle odbc
    解决“指定的服务已经标记为删除”问题
    mysql系列-安装及服务启动
    数据缓存管理
    redis-在乌班图下设置自动启动
    redis-配置文件
    redis安装
    linux-用户建立及权限分配
  • 原文地址:https://www.cnblogs.com/zlhdbk/p/10590062.html
Copyright © 2011-2022 走看看