如何详细解释并理解数学中的逆元概念及其应用?

2026-04-02 02:551阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2762个文字,预计阅读时间需要12分钟。

如何详细解释并理解数学中的逆元概念及其应用?

今天我们来探讨逆元在ACM-ICPC竞赛中的应用。逆元是一个非常重要的概念,必须学会使用。

对于整数a和模m,如果存在整数b,使得a*b ≡ 1 (mod m),那么b就是a在模m下的逆元。

例如,对于整数5和模11,我们可以找到其逆元是9,因为5*9 ≡ 1 (mod 11)。

在算法中,逆元常用于解决诸如求逆元、模逆、求逆序元等问题。




今天我们来探讨逆元在ACM-ICPC竞赛中的应用,逆元是一个很重要的概念,必须学会使用它。


对于正整数


,如果有

,那么把这个同余方程中

的最小正整数解叫做


的逆元。


逆元一般用扩展欧几里得算法来求得,如果

为素数,那么还可以根据费马小定理得到逆元为



推导过程如下


求现在来看一个逆元最常见问题,求如下表达式的值(已知





当然这个经典的问题有很多方法,最常见的就是扩展欧几里得,如果

是素数,还可以用费马小定理。


但是你会发现费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求


互素。实际上我们还有一

种通用的求逆元方法,适合所有情况。公式如下




现在我们来证明它,已知

,证明步骤如下




接下来来实战一下,看几个关于逆元的题目。


题目:​​poj.org/problem?id=1845​​

题意:给定两个正整数


,求

的所有因子和对9901取余后的值。


分析:很容易知道,先把

分解得到

,那么得到

,那么

的所有因子和的表达式如下




所以我们有两种做法。第一种做法是二分求等比数列之和。

代码:


[cpp] ​​view plain​​ ​​copy​​

​​​ ​​​

  1. #include<iostream>
  2. #include<string.h>
  3. #include<stdio.h>
  4. usingnamespacestd;
  5. typedeflonglongLL;
  6. constintN=10005;
  7. constintMOD=9901;
  8. boolprime[N];
  9. intp[N];
  10. intcnt;
  11. voidisprime()
  12. {
  13. cnt=0;
  14. true,sizeof(prime));
  15. for(inti=2;i<N;i++)
  16. {
  17. if(prime[i])
  18. {
  19. p[cnt++]=i;
  20. for(intj=i+i;j<N;j+=i)
  21. false;
  22. }
  23. }
  24. }
  25. LLpower(LLa,LLb)
  26. {
  27. LLans=1;
  28. a%=MOD;
  29. while(b)
  30. {
  31. if(b&1)
  32. {
  33. ans=ans*a%MOD;
  34. b--;
  35. }
  36. b>>=1;
  37. a=a*a%MOD;
  38. }
  39. returnans;
  40. }
  41. LLsum(LLa,LLn)
  42. {
  43. if(n==0)return1;
  44. LLt=sum(a,(n-1)/2);
  45. if(n&1)
  46. {
  47. LLcur=power(a,(n+1)/2);
  48. t=(t+t%MOD*cur%MOD)%MOD;
  49. }
  50. else
  51. {
  52. LLcur=power(a,(n+1)/2);
  53. t=(t+t%MOD*cur%MOD)%MOD;
  54. t=(t+power(a,n))%MOD;
  55. }
  56. returnt;
  57. }
  58. voidSolve(LLA,LLB)
  59. {
  60. LLans=1;
  61. for(inti=0;p[i]*p[i]<=A;i++)
  62. {
  63. if(A%p[i]==0)
  64. {
  65. intnum=0;
  66. while(A%p[i]==0)
  67. {
  68. num++;
  69. A/=p[i];
  70. }
  71. ans*=sum(p[i],num*B)%MOD;
  72. ans%=MOD;
  73. }
  74. }
  75. if(A>1)
  76. {
  77. ans*=sum(A,B)%MOD;
  78. ans%=MOD;
  79. }
  80. cout<<ans<<endl;
  81. }
  82. intmain()
  83. {
  84. LLA,B;
  85. isprime();
  86. while(cin>>A>>B)
  87. Solve(A,B);
  88. return0;
  89. }



第二种方法就是用等比数列求和公式,但是要用逆元。用如下公式即可



因为

可能会很大,超过int范围,所以在快速幂时要二分乘法。


代码:


[cpp] ​​view plain​​ ​​copy​​

​​​ ​​​

  1. #include<iostream>
  2. #include<string.h>
  3. #include<stdio.h>
  4. usingnamespacestd;
  5. typedeflonglongLL;
  6. constintN=10005;
  7. constintMOD=9901;
  8. boolprime[N];
  9. intp[N];
  10. intcnt;
  11. voidisprime()
  12. {
  13. cnt=0;
  14. true,sizeof(prime));
  15. for(inti=2;i<N;i++)
  16. {
  17. if(prime[i])
  18. {
  19. p[cnt++]=i;
  20. for(intj=i+i;j<N;j+=i)
  21. false;
  22. }
  23. }
  24. }
  25. LLmulti(LLa,LLb,LLm)
  26. {
  27. LLans=0;
  28. a%=m;
  29. while(b)
  30. {
  31. if(b&1)
  32. {
  33. ans=(ans+a)%m;
  34. b--;
  35. }
  36. b>>=1;
  37. a=(a+a)%m;
  38. }
  39. returnans;
  40. }
  41. LLquick_mod(LLa,LLb,LLm)
  42. {
  43. LLans=1;
  44. a%=m;
  45. while(b)
  46. {
  47. if(b&1)
  48. {
  49. ans=multi(ans,a,m);
  50. b--;
  51. }
  52. b>>=1;
  53. a=multi(a,a,m);
  54. }
  55. returnans;
  56. }
  57. voidSolve(LLA,LLB)
  58. {
  59. LLans=1;
  60. for(inti=0;p[i]*p[i]<=A;i++)
  61. {
  62. if(A%p[i]==0)
  63. {
  64. intnum=0;
  65. while(A%p[i]==0)
  66. {
  67. num++;
  68. A/=p[i];
  69. }
  70. LLM=(p[i]-1)*MOD;
  71. ans*=(quick_mod(p[i],num*B+1,M)+M-1)/(p[i]-1);
  72. ans%=MOD;
  73. }
  74. }
  75. if(A>1)
  76. {
  77. LLM=MOD*(A-1);
  78. ans*=(quick_mod(A,B+1,M)+M-1)/(A-1);
  79. ans%=MOD;
  80. }
  81. cout<<ans<<endl;
  82. }
  83. intmain()
  84. {
  85. LLA,B;
  86. isprime();
  87. while(cin>>A>>B)
  88. Solve(A,B);
  89. return0;
  90. }



其实有些题需要用到


的所有逆元,这里

为奇质数。那么如果用快速幂求时间复杂度为

,如果对于一个1000000级别的素数

,这样做的时间复杂度是很高了。实际上有

的算法,有一个递推式如下



它的推导过程如下,设

,那么




对上式两边同时除

,进一步得到




再把


替换掉,最终得到




初始化

,这样就可以通过递推法求出

模奇素数

的所有逆元了。


另外


的所有逆元值对应

中所有的数,比如

,那么

对应的逆元是




题目:​​www.lydsy.com/JudgeOnline/problem.php?id=2186​​

题意:求


互质的数的个数,其中



分析:因为

,所以

,我们很容易知道如下结论

对于两个正整数

,如果

的倍数,那么

中与

互素的数的个数为

本结论是很好证明的,因为

中与

互素的个数为

,又知道

,所以

如何详细解释并理解数学中的逆元概念及其应用?

结论成立。那么对于本题,答案就是




其中

为小于等于

的所有素数,先筛选出来即可。由于最终答案对一个质数取模,所以要用逆元,这里

求逆元就有技巧了,用刚刚介绍的递推法预处理,否则会TLE的。


代码:


[cpp] ​​view plain​​ ​​copy​​

​​​ ​​​

  1. #include<iostream>
  2. #include<string.h>
  3. #include<stdio.h>
  4. #include<bitset>
  5. usingnamespacestd;
  6. typedeflonglongLL;
  7. constintN=10000005;
  8. bitset<N>prime;
  9. voidisprime()
  10. {
  11. prime.set();
  12. for(inti=2;i<N;i++)
  13. {
  14. if(prime[i])
  15. {
  16. for(intj=i+i;j<N;j+=i)
  17. false;
  18. }
  19. }
  20. }
  21. LLans1[N],ans2[N];
  22. LLinv[N];
  23. intmain()
  24. {
  25. isprime();
  26. intMOD,m,n,T;
  27. "%d%d",&T,&MOD);
  28. ans1[0]=1;
  29. for(inti=1;i<N;i++)
  30. ans1[i]=ans1[i-1]*i%MOD;
  31. inv[1]=1;
  32. for(inti=2;i<N;i++)
  33. {
  34. if(i>=MOD)break;
  35. inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
  36. }
  37. ans2[1]=1;
  38. for(inti=2;i<N;i++)
  39. {
  40. if(prime[i])
  41. {
  42. ans2[i]=ans2[i-1]*(i-1)%MOD;
  43. ans2[i]=ans2[i]*inv[i%MOD]%MOD;
  44. }
  45. else
  46. {
  47. ans2[i]=ans2[i-1];
  48. }
  49. }
  50. while(T--)
  51. {
  52. "%d%d",&n,&m);
  53. LLans=ans1[n]*ans2[m]%MOD;
  54. "%lld\n",ans);
  55. }
  56. return0;
  57. }



接下来还有一个关于逆元的有意思的题目,描述如下




证明:由




其中


所以只需要证明

,而我们知道


的逆元对应全部

中的所有数,既是单射也是满射。


所以进一步得到




证明完毕!




本文共计2762个文字,预计阅读时间需要12分钟。

如何详细解释并理解数学中的逆元概念及其应用?

今天我们来探讨逆元在ACM-ICPC竞赛中的应用。逆元是一个非常重要的概念,必须学会使用。

对于整数a和模m,如果存在整数b,使得a*b ≡ 1 (mod m),那么b就是a在模m下的逆元。

例如,对于整数5和模11,我们可以找到其逆元是9,因为5*9 ≡ 1 (mod 11)。

在算法中,逆元常用于解决诸如求逆元、模逆、求逆序元等问题。




今天我们来探讨逆元在ACM-ICPC竞赛中的应用,逆元是一个很重要的概念,必须学会使用它。


对于正整数


,如果有

,那么把这个同余方程中

的最小正整数解叫做


的逆元。


逆元一般用扩展欧几里得算法来求得,如果

为素数,那么还可以根据费马小定理得到逆元为



推导过程如下


求现在来看一个逆元最常见问题,求如下表达式的值(已知





当然这个经典的问题有很多方法,最常见的就是扩展欧几里得,如果

是素数,还可以用费马小定理。


但是你会发现费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求


互素。实际上我们还有一

种通用的求逆元方法,适合所有情况。公式如下




现在我们来证明它,已知

,证明步骤如下




接下来来实战一下,看几个关于逆元的题目。


题目:​​poj.org/problem?id=1845​​

题意:给定两个正整数


,求

的所有因子和对9901取余后的值。


分析:很容易知道,先把

分解得到

,那么得到

,那么

的所有因子和的表达式如下




所以我们有两种做法。第一种做法是二分求等比数列之和。

代码:


[cpp] ​​view plain​​ ​​copy​​

​​​ ​​​

  1. #include<iostream>
  2. #include<string.h>
  3. #include<stdio.h>
  4. usingnamespacestd;
  5. typedeflonglongLL;
  6. constintN=10005;
  7. constintMOD=9901;
  8. boolprime[N];
  9. intp[N];
  10. intcnt;
  11. voidisprime()
  12. {
  13. cnt=0;
  14. true,sizeof(prime));
  15. for(inti=2;i<N;i++)
  16. {
  17. if(prime[i])
  18. {
  19. p[cnt++]=i;
  20. for(intj=i+i;j<N;j+=i)
  21. false;
  22. }
  23. }
  24. }
  25. LLpower(LLa,LLb)
  26. {
  27. LLans=1;
  28. a%=MOD;
  29. while(b)
  30. {
  31. if(b&1)
  32. {
  33. ans=ans*a%MOD;
  34. b--;
  35. }
  36. b>>=1;
  37. a=a*a%MOD;
  38. }
  39. returnans;
  40. }
  41. LLsum(LLa,LLn)
  42. {
  43. if(n==0)return1;
  44. LLt=sum(a,(n-1)/2);
  45. if(n&1)
  46. {
  47. LLcur=power(a,(n+1)/2);
  48. t=(t+t%MOD*cur%MOD)%MOD;
  49. }
  50. else
  51. {
  52. LLcur=power(a,(n+1)/2);
  53. t=(t+t%MOD*cur%MOD)%MOD;
  54. t=(t+power(a,n))%MOD;
  55. }
  56. returnt;
  57. }
  58. voidSolve(LLA,LLB)
  59. {
  60. LLans=1;
  61. for(inti=0;p[i]*p[i]<=A;i++)
  62. {
  63. if(A%p[i]==0)
  64. {
  65. intnum=0;
  66. while(A%p[i]==0)
  67. {
  68. num++;
  69. A/=p[i];
  70. }
  71. ans*=sum(p[i],num*B)%MOD;
  72. ans%=MOD;
  73. }
  74. }
  75. if(A>1)
  76. {
  77. ans*=sum(A,B)%MOD;
  78. ans%=MOD;
  79. }
  80. cout<<ans<<endl;
  81. }
  82. intmain()
  83. {
  84. LLA,B;
  85. isprime();
  86. while(cin>>A>>B)
  87. Solve(A,B);
  88. return0;
  89. }



第二种方法就是用等比数列求和公式,但是要用逆元。用如下公式即可



因为

可能会很大,超过int范围,所以在快速幂时要二分乘法。


代码:


[cpp] ​​view plain​​ ​​copy​​

​​​ ​​​

  1. #include<iostream>
  2. #include<string.h>
  3. #include<stdio.h>
  4. usingnamespacestd;
  5. typedeflonglongLL;
  6. constintN=10005;
  7. constintMOD=9901;
  8. boolprime[N];
  9. intp[N];
  10. intcnt;
  11. voidisprime()
  12. {
  13. cnt=0;
  14. true,sizeof(prime));
  15. for(inti=2;i<N;i++)
  16. {
  17. if(prime[i])
  18. {
  19. p[cnt++]=i;
  20. for(intj=i+i;j<N;j+=i)
  21. false;
  22. }
  23. }
  24. }
  25. LLmulti(LLa,LLb,LLm)
  26. {
  27. LLans=0;
  28. a%=m;
  29. while(b)
  30. {
  31. if(b&1)
  32. {
  33. ans=(ans+a)%m;
  34. b--;
  35. }
  36. b>>=1;
  37. a=(a+a)%m;
  38. }
  39. returnans;
  40. }
  41. LLquick_mod(LLa,LLb,LLm)
  42. {
  43. LLans=1;
  44. a%=m;
  45. while(b)
  46. {
  47. if(b&1)
  48. {
  49. ans=multi(ans,a,m);
  50. b--;
  51. }
  52. b>>=1;
  53. a=multi(a,a,m);
  54. }
  55. returnans;
  56. }
  57. voidSolve(LLA,LLB)
  58. {
  59. LLans=1;
  60. for(inti=0;p[i]*p[i]<=A;i++)
  61. {
  62. if(A%p[i]==0)
  63. {
  64. intnum=0;
  65. while(A%p[i]==0)
  66. {
  67. num++;
  68. A/=p[i];
  69. }
  70. LLM=(p[i]-1)*MOD;
  71. ans*=(quick_mod(p[i],num*B+1,M)+M-1)/(p[i]-1);
  72. ans%=MOD;
  73. }
  74. }
  75. if(A>1)
  76. {
  77. LLM=MOD*(A-1);
  78. ans*=(quick_mod(A,B+1,M)+M-1)/(A-1);
  79. ans%=MOD;
  80. }
  81. cout<<ans<<endl;
  82. }
  83. intmain()
  84. {
  85. LLA,B;
  86. isprime();
  87. while(cin>>A>>B)
  88. Solve(A,B);
  89. return0;
  90. }



其实有些题需要用到


的所有逆元,这里

为奇质数。那么如果用快速幂求时间复杂度为

,如果对于一个1000000级别的素数

,这样做的时间复杂度是很高了。实际上有

的算法,有一个递推式如下



它的推导过程如下,设

,那么




对上式两边同时除

,进一步得到




再把


替换掉,最终得到




初始化

,这样就可以通过递推法求出

模奇素数

的所有逆元了。


另外


的所有逆元值对应

中所有的数,比如

,那么

对应的逆元是




题目:​​www.lydsy.com/JudgeOnline/problem.php?id=2186​​

题意:求


互质的数的个数,其中



分析:因为

,所以

,我们很容易知道如下结论

对于两个正整数

,如果

的倍数,那么

中与

互素的数的个数为

本结论是很好证明的,因为

中与

互素的个数为

,又知道

,所以

如何详细解释并理解数学中的逆元概念及其应用?

结论成立。那么对于本题,答案就是




其中

为小于等于

的所有素数,先筛选出来即可。由于最终答案对一个质数取模,所以要用逆元,这里

求逆元就有技巧了,用刚刚介绍的递推法预处理,否则会TLE的。


代码:


[cpp] ​​view plain​​ ​​copy​​

​​​ ​​​

  1. #include<iostream>
  2. #include<string.h>
  3. #include<stdio.h>
  4. #include<bitset>
  5. usingnamespacestd;
  6. typedeflonglongLL;
  7. constintN=10000005;
  8. bitset<N>prime;
  9. voidisprime()
  10. {
  11. prime.set();
  12. for(inti=2;i<N;i++)
  13. {
  14. if(prime[i])
  15. {
  16. for(intj=i+i;j<N;j+=i)
  17. false;
  18. }
  19. }
  20. }
  21. LLans1[N],ans2[N];
  22. LLinv[N];
  23. intmain()
  24. {
  25. isprime();
  26. intMOD,m,n,T;
  27. "%d%d",&T,&MOD);
  28. ans1[0]=1;
  29. for(inti=1;i<N;i++)
  30. ans1[i]=ans1[i-1]*i%MOD;
  31. inv[1]=1;
  32. for(inti=2;i<N;i++)
  33. {
  34. if(i>=MOD)break;
  35. inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
  36. }
  37. ans2[1]=1;
  38. for(inti=2;i<N;i++)
  39. {
  40. if(prime[i])
  41. {
  42. ans2[i]=ans2[i-1]*(i-1)%MOD;
  43. ans2[i]=ans2[i]*inv[i%MOD]%MOD;
  44. }
  45. else
  46. {
  47. ans2[i]=ans2[i-1];
  48. }
  49. }
  50. while(T--)
  51. {
  52. "%d%d",&n,&m);
  53. LLans=ans1[n]*ans2[m]%MOD;
  54. "%lld\n",ans);
  55. }
  56. return0;
  57. }



接下来还有一个关于逆元的有意思的题目,描述如下




证明:由




其中


所以只需要证明

,而我们知道


的逆元对应全部

中的所有数,既是单射也是满射。


所以进一步得到




证明完毕!