扩展欧几里得、求乘法逆元及其应用、中国剩余定理(互质版和非互质版)、欧拉函数、快速判素数模板

互质版:

#include <iostream>  
#include <cstdio>  
#include <cstring>  
using namespace std;  
typedef __int64 int64;  
int64 a[15],b[15];  
  
int64 Extend_Euclid(int64 a, int64 b, int64&x, int64& y)  
{  
    if(b==0)  
    {  
        x=1,y=0;  
        return a;  
    }  
    int64 d = Extend_Euclid(b,a%b,x,y);  
    int64 t = x;  
    x = y;  
    y = t - a/b*y;  
    return d;  
}  
//求解模线性方程组x=ai(mod ni)  
int64 China_Reminder(int len, int64* a, int64* n)  
{  
    int i;  
    int64 N = 1;  
    int64 result = 0;  
    for(i = 0; i < len; i++)  
        N = N*n[i];  
    for(i = 0; i < len; i++)  
    {  
        int64 m = N/n[i];  
        int64 x,y;  
        Extend_Euclid(m,n[i],x,y);  
        x = (x%n[i]+n[i])%n[i];  
        result = (result + m*a[i]*x%N)%N;  
    }  
    return result;  
}  
  
int main()  
{  
    int n;  
    while(scanf("%d",&n)!=EOF)  
    {  
        for(int i = 0; i < n; i++)  
            scanf("%I64d %I64d",&a[i],&b[i]);  
        printf("%I64d\n",China_Reminder(n,b,a));  
    }  
    return 0;  
}  

非互质版:

/** 
中国剩余定理(不互质) 
*/  
#include <iostream>  
#include <cstdio>  
#include <cstring>  
using namespace std;  
typedef __int64 int64;  
int64 Mod;  
  
int64 gcd(int64 a, int64 b)  
{  
    if(b==0)  
        return a;  
    return gcd(b,a%b);  
}  
  
int64 Extend_Euclid(int64 a, int64 b, int64&x, int64& y)  
{  
    if(b==0)  
    {  
        x=1,y=0;  
        return a;  
    }  
    int64 d = Extend_Euclid(b,a%b,x,y);  
    int64 t = x;  
    x = y;  
    y = t - a/b*y;  
    return d;  
}  
  
//a在模n乘法下的逆元,没有则返回-1  
int64 inv(int64 a, int64 n)  
{  
    int64 x,y;  
    int64 t = Extend_Euclid(a,n,x,y);  
    if(t != 1)  
        return -1;  
    return (x%n+n)%n;  
}  
  
//将两个方程合并为一个  
bool merge(int64 a1, int64 n1, int64 a2, int64 n2, int64& a3, int64& n3)  
{  
    int64 d = gcd(n1,n2);  
    int64 c = a2-a1;  
    if(c%d)  
        return false;  
    c = (c%n2+n2)%n2;  
    c /= d;  
    n1 /= d;  
    n2 /= d;  
    c *= inv(n1,n2);  
    c %= n2;  
    c *= n1*d;  
    c += a1;  
    n3 = n1*n2*d;  
    a3 = (c%n3+n3)%n3;  
    return true;  
}  
  
//求模线性方程组x=ai(mod ni),ni可以不互质  
int64 China_Reminder2(int len, int64* a, int64* n)  
{  
    int64 a1=a[0],n1=n[0];  
    int64 a2,n2;  
    for(int i = 1; i < len; i++)  
    {  
        int64 aa,nn;  
        a2 = a[i],n2=n[i];  
        if(!merge(a1,n1,a2,n2,aa,nn))  
            return -1;  
        a1 = aa;  
        n1 = nn;  
    }  
    Mod = n1;  
    return (a1%n1+n1)%n1;  
}  
int64 a[1000],b[1000];  
int main()  
{  
    int i;  
    int k;  
    while(scanf("%d",&k)!=EOF)  
    {  
        for(i = 0; i < k; i++)  
            scanf("%I64d %I64d",&a[i],&b[i]);  
        printf("%I64d\n",China_Reminder2(k,b,a));  
    }  
    return 0;  
}

/*接下来介绍乘法逆元:
若m≥1,gcd(a,m)=1,则存在c使得 ca≡1(mod m) 我们把c称为是a对模m的逆,记为 a-1(mod m)或a-1 可以用扩展欧几里德算法求a-1
应用: 求(a/b)%c时,若a为大整数时可以写成 ((a%c)*((b-1)%c))%c,注意:这里(b-1)是b%c的乘法逆元,不是b减 1!!! 
还可以使用扩展欧几里德算法求乘法逆元:*/
//a在模n乘法下的逆元,没有则返回-1(a * b % n == 1,已知a,n,求b就是求a模n的乘法逆元)

乘法逆元:

int inv(int a, int n)///a在模n乘法下的逆元,没有则返回-1(a * b % n == 1,已知a,n,求b就是乘法逆元)
{  
    int x, y;  
    int t = Ext_gcd(a, n, x, y);///扩展欧几里得算法
    if(t != 1)  
        return -1;  
    return (x % n + n) % n;  
} 

欧拉函数:(E(k) = [1,n-1]中与n互质的整数个数
递推求欧拉函数:

void Euler()
{
    int i, j;
    for (i = 1; i <= Max; i++)
        num[i] = i;
    for (i = 2; i <= Max; i += 2)
        num[i] /= 2;
    for (i = 3; i <= Max; i += 2)
    {
        if(num[i] == i)
        {
            for (j = i; j <= Max; j += i)
                num[j] = num[j] / i * (i - 1);
        }
    }
}

单独求欧拉函数:

int Euler(int x)
{
    int res = x;
    for(int i = 2; i < (int)sqrt(x * 1.0) + 1; i++)
    {
        if(x % i == 0)
        {
            res = res / i * (i - 1);
            while (x % i == 0)/// 保证i一定是素数
                x /= i;
        }
    }
    if(x > 1)
        res = res / x * (x - 1);
    return res;
}


避免TLE的判素数方法模板:
先打素数表再判是否为素数:

const int N = 100005;
const int MOD = 9901;
LL n,ans;
bool prime[N];
int p[N];//保存素数 
int cnt;
void isprime()//素数筛选 
{
    cnt = 0;
    memset(prime,true,sizeof(prime));
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            p[cnt++] = i;
            for(int j=i+i; j<N; j+=i)
                prime[j] = false;
        }
    }
}
bool Judge(LL A)//判断是否为素数,是素数则返回true 
{
    for(int i=0; p[i]*p[i] <= A; i++)
    {
        if(A % p[i] == 0)
        {
            return false;
        }
    }
    return true;
}



<think>嗯,用户之前问过关于ElGamal数字签名算法的代码实现,现在又想知道扩展欧几里得算法逆元的代码实现。看起来他可能是在学习密码学中的算法,特别是涉及到模逆元的部分,比如在ElGamal签名中的s值计算。用户可能需要具体的代码示例,以及背后的数学原理,来更好地理解如何在实际中应用这些概念。 首先,我需要确认用户对扩展欧几里得算法的了解程度。可能他们已经知道欧几里得算法用于最大公约数,但扩展本还能找到贝祖系数,从而计算模逆元。用户可能需要一个清晰的步骤解释,以及如何将其转化为代码。 接下来,我需要考虑代码实现的细节。比如,如何处理递归迭代两种方法,如何返回逆元,以及在存在逆元时的处理。用户提供的ElGamal代码中使用的是OpenSSL的BN_mod_inverse函数,但用户现在可能希望自己实现这个功能,以加深理解。 还要注意数学部分的正确性,比如在模运算中,逆元存在的条件是am互质。因此,在代码中需要检查这一点,否则返回错误或特定值。同时,递归迭代方法的效率可能也是一个考虑点,但用户可能更关注正确性可读性。 用户可能还需要示例来验证代码的正确性,比如用已知的逆元例子测试代码。此外,解释数学原理如何转化为代码逻辑,比如贝祖等式中的系数递推,以及如何处理负数结果的情况,将其转换为正数模数。 最后,确保回答结构清晰,分步骤说明,结合数学代码,可能还要比较递归迭代的优缺点,帮助用户选择适合的实现方式。同时,提醒用户在实际应用中使用经过验证的库函数,比如OpenSSL的,以避免安全漏洞。</think>以下是关于扩展欧几里得算法逆元的详细解析与代码实现,包含数学原理、代码示例关键注意事项: --- ### **一、数学原理** 设 $a$ 在模 $m$ 下的逆元 $a^{-1}$,需满足: $$ a \cdot a^{-1} \equiv 1 \ (\text{mod} \ m) $$ **核心方程**(贝祖等式): $$ ax + my = \gcd(a, m) $$ 当 $\gcd(a, m)=1$ 时,$x$ 即为所逆元 --- ### **二、代码实现(C++)** #### **本1:递归实现** ```cpp int exgcd(int a, int b, int &x, int &y) { if (b == 0) { x = 1; y = 0; return a; } int g = exgcd(b, a % b, y, x); // 递归调用 y -= a / b * x; // 更新y的值 return g; } int mod_inverse(int a, int m) { int x, y; int g = exgcd(a, m, x, y); if (g != 1) return -1; // 逆元存在 return (x % m + m) % m; // 保证结果为正 } ``` #### **本2:迭代实现**(性能更优) ```cpp int mod_inverse_iter(int a, int m) { int old_r = a, r = m; int old_s = 1, s = 0; while (r != 0) { int quotient = old_r / r; // 更新系数 int temp = r; r = old_r - quotient * r; old_r = temp; temp = s; s = old_s - quotient * s; old_s = temp; } if (old_r != 1) return -1; // 无解 return (old_s % m + m) % m; // 标准化结果 } ``` --- ### **三、算法执行流程示例** **示例**: $3^{-1} \ (\text{mod} \ 11)$ **步骤追踪表**: | 步骤 | a | b | q | x | y | |------|-----|-----|-----|-----|-----| | 初始 | 11 | 3 | 3 | 0 | 1 | | 1 | 3 | 2 | 1 | 1 | -3 | | 2 | 2 | 1 | 2 | -1 | 4 | | 3 | 1 | 0 | - | 4 | -11 | **结果验证**: $3 \times 4 = 12 \equiv 1 \ (\text{mod} \ 11)$ --- ### **四、关键特性对比** | 特性 | 递归 | 迭代 | |---------------|---------------------------|---------------------------| | 时间复杂度 | $O(\log \min(a,m))$ | 相同 | | 空间复杂度 | $O(\log \min(a,m))$ 栈空间 | $O(1)$ | | 溢出风险 | 较高(深递归) | 较低 | | 可读性 | 更直观 | 需要理解状态维护 | --- ### **五、边界条件处理** 1. **负数输入**: ```cpp a = (a % m + m) % m; // 预处理保证a∈[0,m) ``` 2. **m=1的特殊情况**: ```cpp if (m == 1) return 0; // 0 ≡ 1 mod 1 ``` 3. **无逆元情况**: ```cpp if (gcd(a, m) != 1) throw "No inverse exists"; ``` --- ### **六、大数运算扩展** 当处理大整数时(如密码学应用),需使用高精度库: ```cpp #include <openssl/bn.h> BIGNUM* bn_mod_inverse(const BIGNUM *a, const BIGNUM *m) { BN_CTX *ctx = BN_CTX_new(); BIGNUM *inv = BN_new(); if (!BN_mod_inverse(inv, a, m, ctx)) { BN_free(inv); inv = NULL; // 表示无解 } BN_CTX_free(ctx); return inv; } ``` --- ### **七、性能优化技巧** 1. **预计算模数**:当需要多次计算同一模数的逆元时,可使用蒙哥马利模乘 2. **二进制扩展欧几里得**:减少除法运算次数 3. **奇模数优化**:当m为奇数时,可用位运算加速 --- ### **八、应用场景** 1. RSA密钥生成:计算 $d ≡ e^{-1} \ (\text{mod} \ φ(n))$ 2. 椭圆曲线加密:有限域逆运算 3. 多项式逆:编码理论中的伽罗华域运算 --- 通过上述代码实现与数学分析,可以深入理解扩展欧几里得算法在模逆元计算中的核心作用。实际开发中建议优先使用经过严格验证的数学库(如OpenSSL的BN_mod_inverse),但在学习阶段手写实现有助于夯实理论基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值