求逆元的三种常用方法 及Striling求n!的近似值

逆元:

定义:对于正整数a,如果有 ax≡1(mod m),注意这里的等号代表恒等,那么把这个同余方程中的最小正整数解x叫作a模m的逆元(其实相当于 x*x^-1=1的变式)。
求逆元常用的有三种方法:扩展欧几里德算法、费马小定理、欧拉定理求逆元。当然还有其他的办法,但是以下只对以上三个进行介绍。

扩展欧几里德算法:
欧几里得算法返回的是两个数的最大公约数,而扩展欧几里德算法返回的也是最大公约数 不过所传的参数多了而已。
定义:用来在已知a,b求解一组x、y使得ax+by=gcd(a,b)(此式子的解一定存在,依据数论中的知识)
常用在求解模线性方程及方程组中。

关于解x、y的方法的理解: 已知方程ax+by=gcd(a,b)
(1)当b=0,gcd(a,b)=a,此时x=1,y=0; 1a+0b=a
(2)设ax1+by1=gcd(a,b)

在下一个exgcd中 bx2+(a mod b)y2=gcd(b,a mod b)
根据欧几里得原理可知 gcd(a,b)=gcd(b,a mod b)
则 ax1+by1=bx2+(a mod b)y2
即ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2;
则x1=y2,y1=x2-(a/b)y2;
所以可以得出x1,y1的值基于x2,y2。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=200;
char s[N];
char b[N];

int exgcd(int a,int b,int &x,int &y)//扩展gcd
{
    if(!b)//b为0的情况
    {
        x=1;
        y=0;
        return a;
    }
    int r=exgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return r;
}


int main()
{

    ios::sync_with_stdio(false);
    int a,b;
    cin>>a>>b;
    int x=0,y=0;
    cout<<exgcd(a,b,x,y)<<endl;

    return 0;
}

1.扩展gcd求逆元要求 a,m互质

求出的逆元是最小逆元

先给出互质的概念,n个数(n>=2)的公因数只有1.
质数又称之为素数。

先给出数学过程:
ax+by=gcd(a,b);
则ax+my=gcd(a,m);
因为a,m互质 所以 gcd(a,m)=1
则 ax+my=1
即 ax≡1(mod m)
此时x为a的逆元。

代码:用扩展gcd求出一组 x,y,gcd;若gcd为1 则逆元存在,将x调整到0~m-1的范围内;否则逆元不存在

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=200;
char s[N];
char b[N];

int exgcd(int a,int b,int &x,int &y)
{
    if(a==0&&b==0)
        return -1;
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    int d=exgcd(b,a%b,y,x); //回代 注意这里的与扩展gcd的不同
    y-=a/b*x;
    return d;
}



int main()
{

    ios::sync_with_stdio(false);
    int m,n;
    cin>>m>>n;
    int x,y;
    int d=exgcd(m,n,x,y);
    cout<<(x%n+n)%n<<endl;

    return 0;
}

2.费马小定理求逆元要求m为素数,且a,m互质

费马小定理: a^(m-1)≡1 (mod m) //m为素数
a^(m-1)≡ 1 (mod m) 则a*a^(m-2)≡1 (mod m)
故 a^(m-2)为a在模m下的逆元(用快速幂求解即可)
时间复杂度 logN。

代码:

int mod;
int quick_mod(int n,int p)
{
    int res=1,k=p;
    while(k)
    {
        if(k%2)
            res=res*n%mod;
        n=n*n%mod;
        k/=2;
    }
    return res;
}



int main()
{

    ios::sync_with_stdio(false);
    int a;
    cin>>a>>mod;
    cout<<quick_mod(a,mod-2)<<endl;
    return 0;
}

3.欧拉定理求逆元:基本通用
时间复杂度 lg(√n)。

欧拉定理: a^(∅ (m))≡ 1 (mod m)
则 a*a^(∅ (m)-1)≡ 1 (mod m)
即 ax≡ 1 (mod m)
故 a^(∅ (m)-1)是a的逆元 ∅ (m)是小于m且与m互质的数的个数

1.如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系
2.对于素数p Φ (p)=p-1,对于两个素数pq ,Φ (pq)=pq-1
欧拉函数是积性函数,但不是完全积性函数
3.如果n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数),则这里写图片描述
比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4。
这是因为只有当一个数不包含质数p,才可能与n互质。而包含质数p的数一共有p(k-1)个,即1×p、2×p、3×p、…、p(k-1)×p,把它们去除,剩下的就是与n互质的数。
上面的式子还可以写成下面的形式:
这里写图片描述
4.如果n可以分为两个互质的整数的乘积,n=p1*p2
则Φ (n)=Φ (p1)*Φ (p2)
5.任意一个数n(n>1)都可以写成一系列质数的积
这里写图片描述
根据4的结论,得到
这里写图片描述
再根据3的结论,得到
这里写图片描述
这里写图片描述

下面给出两种求欧拉数的代码:

直接求欧拉:

int euler(int x)
{
    int res=x;
    for(int i=2;i*i<=x;i++)
        if(x%i==0)
        {
            res=res/i*(i-1);
            while(x%i==0)
                x/=i;
        }
     if(x>1)
        res=res/x*(x-1);
     return res;
} 

也可以先打表直接使用,过程是想让每个phi【i】=i,再对每个质数p,j为它的倍数,phi【j】=phi【j】/p*(p-1);

void eulerplus()
{
    for(int i=1;i<=N;i++)
        phi[i]=i;
    for(int i=2;i<=N;i+=2)
        phi[i]/=2;
    for(int i=3;i<=N;i+=2)
        if(phi[i]==i)
    {
        for(int j=i;j<=N;j+=i)
            phi[j]=phi[j]/i*(i-1);
    }
}

4.逆元的应用 求模逆元
ax≡ 1 (mod m)
故 x=1/a(mod m)
故 (b/a)mod m
则(bx)mod m
则(b%m
x%m)%m

striling公式

常用于求n!近似值
n!≈ √2πn(n/e)^n

emmm 还是上图片吧
这里写图片描述

该知识点可以与lg连用估算某数的位数
n进制数x的位数=logn(x)+1
例如十进制数x的位数=lg(x)+1

还可以估算某数的阶乘是另一个数的倍数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值