• 数学-剩余系


    欧几里得算法与扩展欧几里得算法

    gcd. 用于求最大公约数.

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

    扩欧. 用于求方程 $ax+by=gcd(a,b)$ 的解$(x,y)$ .

    struct value{ int x,y; value(int x,int y):x(x),y(y){} };
    value ExtEuclid(int a,int b)
    {
    if(a==0) return value(0,1);
        value r=ExtEuclid(b%a,a);
        return value(r.y-b/a*r.x,r.x);
    }

    有解的充要条件:

    $$ gcd(a,b)|c $$

    它的解为

    $$ (x,y)frac{c}{gcd(a,b)} $$

    $(x,y)$ 由 $ExEuclid(a,b)$ 的返回值给出.

     

     

     

    非递归写法

    gcd

    int gcd(int a,int b)
    { while(a) b=b%a,swap(a,b); return b; }

    扩欧的不会.....

     

     

     

     

    求剩余系中某数的乘法逆元

    考虑剩余系下的除法 $ a/b=ab^{-1} space space (mod space MOD) $ ,有 $ a^{-1} a=1 space space (mod space MOD) $

    这个算法求出$a^{-1}$ ........

    注意只有模数为质数时,整个剩余系的除零以外其它元素才都会有逆元.

    int getrev(int p)
    { return p==0 ? -1 : (ExtEuclid(p,MOD).x%MOD+MOD)%MOD; }

    具体的,要求 $ ax equiv 1 space space (mod space MOD) $ 中的x,有 $ ax+MODy=1 $

    如果a与MOD的gcd不为1,则逆元不存在.

    直接用扩欧来算(x,y)就行了......

     

     

     

     

    线性同余方程组

    考虑方程组

    $$ left{egin{matrix}
    {x equiv a_1 ; ( mod ; m_1 ) }\
    {x equiv a_2 ; ( mod ; m_2 ) }\
    { ...... }\
    {x equiv a_n ; ( mod ; m_n ) }
    end{matrix} ight. $$

     其中

    $$ forall_{i,j} quad a_i equiv a_j ; (mod ; gcd(m_i,m_j))  quad (有解条件) $$

    并且

    $$ forall_{i,j} quad gcd(m_i,m_j)=1 quad (中国剩余定理应用条件) $$

     

    • 使用中国剩余定理构造解x的方法如下

    设 $ M=prod m_i $

    设 $ M_i = M/m_i $

    设 $ t_i = M_i ^{-1} (mod ; m_i) $ 即 $t_i$ 为 $M_i$ 在模 $m_i$ 剩余系下的逆元.

    则 $ x=sum t_i a_i M_i $

    AC VIJOS 1164 裸的中国剩余定理.

     1 int n;
     2 ll a[20],m[20];
     3 ll t;
     4 
     5 struct value{ ll x,y; value(ll x,ll y):x(x),y(y){} };
     6 value ExEuclid(ll a,ll b)
     7 {
     8     if(!a) return value(1,1);
     9     value r=ExEuclid(b%a,a);
    10     return value(r.y-b/a*r.x,r.x);
    11 }
    12 ll rev(ll k,ll m)
    13 { return (ExEuclid(k%m,m).x%m+m)%m; }
    14 
    15 int main()
    16 {
    17     n=getint();
    18     for(int i=0;i<n;i++)
    19     scanf("%I64d%I64d",m+i,a+i);
    20     
    21     t=1;
    22     for(int i=0;i<n;i++)
    23     t*=m[i];
    24     
    25     ll res=0;
    26     for(int i=0;i<n;i++)
    27     res+=t/m[i]*rev(t/m[i],m[i])*a[i];
    28     
    29     printf("%I64d
    ",res%t);
    30     return 0;
    31 }
    View Code

    注意 $M$ 的值会非常大,考虑是否需要使用高精度.

     

     

    应用的时候不要混淆解的判定条件以及定理应用条件!

    考虑方程组

    $$ x equiv 0 ; (mod ; 2) \ xequiv 2 ;(mod ; 4) $$

    显然有解 $x=2,6,....$

    而按照上述解法,有 $M_1=4$ ,因而 $M_1 ; mod ; m_1 = 0 $ ,这样 $M_1$ 就不存在逆元 $t_1$ .

     

    • 如何得到一般方程组的解呢?

    考虑把方程依次加入.

    先看第一个方程 $$ x ; mod ; m_1 = a_1 $$

    它的解是 $$ x=a_1+km_1 ; ; , ; k in N quad $$

    现有解 $$ x_1 leftarrow a_1+km_1  $$

    当我们加入第$i$个方程时,

    $$ x_i leftarrow x_{i-1}+k imes lcm(m_1,m_2,...,m_{i-1}) $$

    其中的 $k$ 满足 $$ x_{i-1} + k imes lcm(m_1,m_2,...,m_{i-1}) ; mod ; m_i = a_i  $$

    亦即 $$ x_{i-1} + k imes lcm(m_1,m_2,...,m_{i-1}) + tm_i = a_i $$

    使用扩展欧几里得算出k即可.

     

    为什么算法是正确的? lcm的意义是什么?

    @iwtwiioi 指出lcm是必要的,这里没有给出lcm的必要性证明.

    呃,这个东西丢到周末去做吧.........在我彻底理解那玩意之后......

    update(3.29):于是还是没改......

    这里有归纳证明: $$ 若 x_i 满足了前i个方程,则由上述算法得出的 x_{i+1} 满足了前i+1个方程. $$

    我们有 $$ k imes lcm(m_1,m_2,...,m_i) ; mod ; m_p = 0 $$ 这是因为 $1 leq p leq i$ ,从而 $m_p mid lcm(m_1,m_2,...,m_i)$

    对于前$i$个方程中的某个方程 $p$ ,必定有 $$ x_i ; mod ; m_p = a_p $$ 这是因为 $x_i$ 满足前i个方程.

    根据上边的式子以及递推过程我们得到

    $$ x_{i+1} ; mod ; m_p \ = ( x_i + k imes lcm(m_1,m_2,...,m_i)) ; mod ; m_p \ = x_i ; mod ; m_p + k imes lcm(m_1,m_2,...,m_i) ; mod ; m_p \ = a_p + 0 \ = a_p $$

    这样 $x_{i+1}$ 就满足前$i$个方程了.我们还剩第$i+1$个方程. 注意我们是按照第 $i+1$ 个方程的约束求出 $k$ 的,很明显 $k$ 的值会使 $x_{i+1}$ 满足方程 $i+1$.

    这样就结束了$=omega=$

     

    一题. AC POJ 2891 解一般的线性模方程组.

     1 inline ll modc(ll k,const ll MOD)
     2 { return (k%MOD+MOD)%MOD; }
     3 
     4 int n;
     5 ll t;
     6 
     7 struct value{ ll x,y; value(ll x,ll y):x(x),y(y){} };
     8 value ExEuclid(ll a,ll b)
     9 {
    10     if(!a) return value(1,1);
    11     value r=ExEuclid(b%a,a);
    12     return value(r.y-b/a*r.x,r.x);
    13 }
    14 
    15 ll rev(ll k,ll m)
    16 { return modc(ExEuclid(k%m,m).x,m); }
    17 
    18 ll gcd(ll a,ll b)
    19 { while(a) b=b%a,swap(a,b); return b; }
    20 
    21 int main()
    22 {
    23     while(scanf("%d",&n)>0)
    24     {
    25         bool ok=1;
    26         ll a,m,x;
    27         m=getll();
    28         a=getll();
    29         
    30         x=a;
    31         ll lcm=m;
    32         
    33         for(int i=1;i<n;i++)
    34         {
    35             m=getll();
    36             a=getll();
    37             
    38             if(!ok) continue;
    39             
    40             ll d=gcd(lcm,m);
    41             
    42             if((a-x)%d!=0) { ok=false; continue; }
    43             
    44             ll k=ExEuclid(lcm,m).x;
    45             k=(a-x)/d*k%(m/d);
    46 
    47             x+=k*lcm;
    48             lcm=m/d*lcm;
    49             x=x%lcm;
    50         }
    51         
    52         if(!ok) printf("-1
    ");
    53         else printf("%lld
    ",modc(x,lcm));
    54     }
    55     
    56     return 0;
    57 }
    View Code

    注意精度问题. 能取模就取模......

    由于是同余恒等式,切记不要搞错符号......

    ExEuclid的变量不是可以随便换的......

     

    再来一题. AC HDU 1573 也是比较裸的同余方程,要求给定范围内解的个数.

    没仔细看题导致WA了一个小时 TAT

    X是正整数啊..... 我把0算进去了啊...... TAT

    View Code

    Baby Step Giant Step (BSGS)

    考虑有一个完全剩余系:$$a^0,a^1,a^2 ; ... ;,a^{p-1} (mod ; p)$$

    我们想求一个 $x$ 使得 $$a^x; equiv ; b ; ; (mod ; p)  $$

    那么,考虑把剩余系按原顺序分成 $m=lceil{sqrt{p}} ceil$ 块,每一块包含元素

    $$ a^{km},a^{km+1},...,a^{km+m-1} $$

    ,即

    $$ a^{km},a^{km}a^1,...,a^{km}a^{m-1} $$

    我们把 $$ ba^{-1},ba^{-2},...,ba^{-m+1} $$ 放入一个双射的表(hash,或者map).

    然后枚举 $k$ 计算 $ a^{km} $ ,在表里面找到与之相等的元素 $ba^{-i}$ ,那么 $km+i$ 就是答案了.......

    嗯,这么做只是因为有如下等式 $$ a^{km} ; equiv ; ba^{-i} ;;(mod; p) $$这是显然的吧.....

    AC BZOJ 2242

    涵盖了常用的数值算法....

    task1: 快速幂

    task2: 扩欧

    task3: 分块(Baby Step Giant Step)

      1 #include <cstdio>
      2 #include <fstream>
      3 #include <iostream>
      4  
      5 #include <cstdlib>
      6 #include <cstring>
      7 #include <algorithm>
      8 #include <cmath>
      9  
     10 #include <queue>
     11 #include <vector>
     12 #include <map>
     13 #include <set>
     14 #include <stack>
     15 #include <list>
     16  
     17 typedef unsigned int uint;
     18 typedef long long int ll;
     19 typedef unsigned long long int ull;
     20 typedef double db;
     21  
     22 using namespace std;
     23  
     24 inline int getint()
     25 {
     26     int res=0;
     27     char c=getchar();
     28     bool mi=false;
     29     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
     30     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
     31     return mi ? -res : res;
     32 }
     33 inline ll getll()
     34 {
     35     ll res=0;
     36     char c=getchar();
     37     bool mi=false;
     38     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
     39     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
     40     return mi ? -res : res;
     41 }
     42 
     43 //==============================================================================
     44 //==============================================================================
     45 //==============================================================================
     46 //==============================================================================
     47 
     48 ll MOD;
     49 
     50 struct pl{ ll x,y; pl(ll a,ll b):x(a),y(b){} };
     51 pl ExEuclid(ll a,ll b)
     52 {
     53     if(!a) return pl(1,1);
     54     pl r=ExEuclid(b%a,a);
     55     return pl(r.y-b/a*r.x,r.x);
     56 }
     57 
     58 ll FastMulti(ll a,ll x)
     59 {
     60     ll res=1;
     61     while(x)
     62     {
     63         if(x&1) (res*=a)%=MOD;
     64         (a*=a)%=MOD;
     65         x>>=1;
     66     }
     67     return res;
     68 }
     69 
     70 ll gcd(ll a,ll b)
     71 { if(a>b)swap(a,b); while(a)b=b%a,swap(a,b); return b; }
     72 
     73 ll rev(ll a)
     74 { return (ExEuclid(a,MOD).x%MOD+MOD)%MOD; }
     75 
     76 map<ll,int> g;
     77 
     78 int main()
     79 {
     80     int T=getint();
     81     int type=getint();
     82     if(type==1) while(T--)
     83     {
     84         ll a=getint();
     85         ll b=getint();
     86         MOD=getint();
     87         printf("%lld
    ",FastMulti(a,b));
     88     }
     89     else if(type==2) while(T--)
     90     {
     91         ll a=getint();
     92         ll c=getint();
     93         MOD=getint();
     94         if(c%gcd(a,MOD)!=0) printf("Orz, I cannot find x!
    ");
     95         else 
     96         printf("%lld
    ",((c/gcd(a,MOD)*ExEuclid(a,MOD).x)%MOD+MOD)%MOD);
     97     }
     98     else if(type==3) while(T--)
     99     {
    100         g.clear();
    101         ll a=getint();
    102         ll b=getint();
    103         MOD=getint();
    104         ll lim=(ll)(ceil(sqrt((long db)MOD)));
    105         ll c=1;
    106         for(int i=0;i<lim;i++)
    107         g[(rev(c)*b%MOD+MOD)%MOD]=i,(c*=a)%=MOD;
    108         ll d=1;
    109         bool found=false;
    110         ll res=0;
    111         for(int i=0;i<=lim;i++)
    112         {
    113             map<ll,int>::iterator v=g.find(d);
    114             if(v!=g.end()) 
    115             { res=(lim*i+v->second)%MOD; found=true; break; } 
    116             else (d*=c)%=MOD;
    117         }
    118         if(!found) printf("Orz, I cannot find x!
    ");
    119         else printf("%lld
    ",(res%MOD+MOD)%MOD);
    120     }
    121     
    122     return 0;
    123 }
    View Code

     

    原根

    考虑一个模 $p$ 剩余系. 我们知道对于 $leq a < p-1$ , $a^1,a^2,...,a^{phi(p)}$ 是 $a$ 在剩余系下的一个循环节.但不一定是最短的.

    我们把这个剩余系下循环节长度刚好是 $phi(p)$ 的数 $r$ 称为这个数的原根.

    模 $p$ 剩余系的充要条件: $p=2$ 或 $p=4$ 或 $p=s^k$ 或 $p=2s$ ,其中 $s$ 是奇素数, $k$ 是任意正整数.

    如何判断 $x$ 是否是原根? 注意 $x^{phi(p)}=p$ 是必然的,不论 $x$ 是什么值.

    于是乎,直接枚举 $d|phi(p)$ ,判断 $x^d=p$ 是否成立.如果有除了 $phi(d)$ 以外的数成立,那么这个数就不是原根.否则就是.

    如何求原根? 从 $2$ 开始逐一枚举然后判断....... 原根一般都很小.....

     

     

     

    未完待续

    模意义下的高斯消元

     

     


    吐槽

     写LaTeX要抓狂了啊啊啊啊啊 $QAQ$

    谁告诉我公式左对齐怎么搞Wiki大法好.....


     

    参考及拓展

    1. EN-WIKI上的CRT条目

     

  • 相关阅读:
    新发地突发新冠病例,我从北京来赶来为疾控软件开发人员分享Azure DevOps,汗
    Azure DevOps Server:还原被删除的分支
    Azure DevOps Server 拉取请求模板(Pull Request Template)
    Azure DevOps Server:使用Postman批量删除测试用例
    Visual Studio Code:在操作系统文件夹或文件上出现Open with Code菜单
    在Windows XP上访问Azure DevOps Server 2019(TFS)
    将团队项目迁移到另外一个团队项目集合中
    快速排序c++实现 快排C++代码实现
    高等代数(第四版) 王萼芳课后答案
    数据结构题集(C语言版)课后答案+源码严蔚敏、吴伟民编著
  • 原文地址:https://www.cnblogs.com/DragoonKiller/p/4355855.html
Copyright © 2020-2023  润新知