Seeking four algorithms of inverse (Rio Oufei Ma small linear push Euler)

Inversion yuan four algorithms

Expand Euclidean algorithm for finding inverse

On a blog we have talked about expanding the Euclidean algorithm, and explain the principle of inverse elements. Here are just codes

When using the inverse number of the required prime p

Code

//扩展欧几里得定理
int ex_gcd(int a,int b,int& x,int& y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    int ans = ex_gcd(b,a%b,x,y);
    int tmp = x;
    x = y;
    y = tmp-a/b*y;
    return ans;
}
int cal(int a,int m)
{
    int x,y;
    int gcd = ex_gcd(a,m,x,y);
    //cout << "a " << a << " m " << m << " x " << x << " y " << y << endl;
    if(1%gcd!=0) return -1;
    x*=1/gcd;
    m = abs(m);
    int ans = x%m;
    if(ans<=0) ans += m;
    return ans;
}

Fermat's little theorem inverse yuan

In p is a prime number of cases , there are \ (A ^ {p-. 1} \ equiv1 (\ MOD p) \) , i.e. \ (A ^ {p-2} A \ equiv1 (\ MOD p) \) . Therefore, the inverse of a modulo p is \ (a-p ^ {2} \) , the available power quickly solved.

Code

//费马小定理
long long q_pow(long long a,long long b,long long p)
{
    long long res = 1;
    while(b)
    {
        if(b&1)
        {
            res = (res*a)%p;
        }
        a = (a*a)%p;
        b>>=1;
    }
    return res;
}
long long inverse(long long a,long long p)
{
    return q_pow(a,p-2,p);
}

Linear Recursive inversing

P is a prime number and a one-time play [1, p 1-] can be used when all inverse element

Formula derivation: Now inverse element k

\ (AK + b = p \) ,

\(b*inv[b]\equiv1\mod p\)

\((p-ak)*inv[b]\equiv1\mod p\)

\((p*inv[b]-ak*inv[b])\equiv1\mod p\)

Since \ (p * inv [b] \ equiv0 \ mod p \)

There \ (- ak * inv [b ] \ equiv1 \ mod p \)

And \ (b = p \% k \)

There \ (- ak * inv [p \% k] \ equiv1 mod p \)

And \ (ak + b = p, so a = p / k (divisible) \)

That \ (- (p / k) * inv [p \% k] * k \ equiv1 \ mod p \)

So there are \ (inv [k] = - (p / k) * inv [p \% k] \)

When using the minus sign to remove the plus p

Code

//线性递推
int inv[max_n];
void ksm(int p)
{
    inv[1] = 1;
    for(int i = 2;i<=p-1;i++)
    {
        inv[i] = (p-p/i)*inv[p%i]%p;
    }
}

Euler's theorem inverse yuan

在p为非质数时使用

欧拉定理表明,a,p互质时,有\(a^{\phi(p)}\equiv1(\mod p)\),则a模p的逆元为\(a^{\phi(p)-1}\)。求出欧拉函数后可用快速幂求得逆元。

代码

原理是\(\phi(n)=n*\prod_{i=1}^k(1-\frac{1}{factor[i]})\)。factor[i]表示n的因子

//欧拉函数
int phi(int x)
{
    int ans = x;
    for(int i = 2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            ans = ans/i*(i-1);
            while(x%i==0) x/=i;
        }
    }
    if(x>1)
        ans=ans/x*(x-1);
    return ans;
}

还有一种欧拉筛法,批量求欧拉函数,类似于埃氏筛

代码

//欧拉筛法
int Phi[max_n];
void euler(int N)
{
    Phi[1] = 1;
    for(int i = 2;i<N;i++)
    {
        if(!Phi[i])
        {
            for(int j = i;j<N;j+=i)
            {
                if(!Phi[j])
                {
                    Phi[j] = j;
                }
                Phi[j] = Phi[j]/i*(i-1);
            }
        }
    }
}

另一种更高效的筛法

原理:

p为质数,\(\phi(p)=p-1\)

if\(i\%p==0\),then \(\phi(i*p)=\phi(i)*p\)

if \(i\%p!=0\),then \(\phi(i*p)=\phi(i)*(p-1)\).

代码

//更快的欧拉筛
int tot = 0;//质数个数
int prime[max_n];//质数
void Euler(int N)
{
    Phi[1] = 1;
    for(int i = 2;i<N;i++)
    {
        if(!Phi[i])
        {
            Phi[i] = i-1;
            prime[tot++] = i;
        }
        for(int j = 0;j<tot&&(long long)i*prime[j]<N;j++)
        {
            if(i%prime[j])
            {
                Phi[i*prime[j]] = Phi[i]*(prime[j]-1);
            }
            else
            {
                Phi[i*prime[j]] = Phi[i]*prime[j];
                break;
            }
        }
    }
}

还有一种似乎表面上原理不同,但实现与上面相似的欧拉筛,这次是直接求出[1,p-1]逆元的那种。

逆元有个性质:

\(inv[a]*inv[b]=inv[a*b]\)

因为\(a*inv[a]\equiv b*inv[b]\equiv1(\mod p)\).

\(ab*inv[a]*inv[b]\equiv1(\mod p)\),

\(inv[a*b]=inv[a]*inv[b]\).

所以对于每个合数 ,我们把所有它的因子的逆元筛出来再相乘即可。

所以我们可以直接把所有素数筛出来,对它们求逆元(拓欧或费马小定理),再把它的逆元乘给它的倍数就可以了。

//另一种欧拉筛
int vis[max_n];
int inv[max_n];
int prime[max_n];//prime[0]记录质数个数
void EUler(int p)
{
    vis[1] = 1;
    inv[1] = 1;
    for(int i = 2;i<=p-1;i++)
    {
        if(!vis[i])
        {
            prime[++prime[0]] = i;
            inv[i] = q_pow(i,p-2,p);
        }
        for(int j=0;j<prime[0];j++)
        {
            if(i*prime[j]>p) break;
            inv[i*prime[j]]=inv[i]*inv[prime[j]];
            if(i%prime[j]) break;
        }
    }
}

写着写着老是有一种“回字的四种写法”的感觉ε=(´ο`*)))

参考文章

x义x的博客,【x义x讲坛】浅谈模质数意义下的乘法逆元,https://www.luogu.org/blog/zyxxs/post-xiao-yi-jiang-tan-qian-tan-sheng-fa-ni-yuan#

镜外,ACM数论之旅7---欧拉函数的证明及代码实现(我会证明都是骗人的╮( ̄▽ ̄)╭),https://www.cnblogs.com/linyujun/p/5194170.html

Rain722,乘法逆元的几种计算方法,https://blog.csdn.net/Rain722/article/details/53170288

hehe_54321,乘法逆元(欧拉函数,欧拉定理,质数筛法),https://www.cnblogs.com/hehe54321/p/7778955.html

Guess you like

Origin www.cnblogs.com/zhanhonhao/p/11330792.html