扩展欧几里得算法 & 线性同余方程

扩展欧几里得算法

c++

扩展欧几里得算法

/*
 * 扩展欧几里得算法
 *
 *  ai×xi+bi×yi=gcd(ai,bi)
 *
 *  之前欧几里得算法已经有过证明,这里简述是如何扩展 gcd 求得方程解的
 *
 *  递归终点,b = 0 时, gcd(a, b) = a
 *      a * 1 + b * 0 = gcd(a, b)
 *      也就是说在递归终点,我们可以得到 gcd(a, b) 的一组解,那么这一组解能否往上推呢?
 *  当并非递归终点,b != 0时候,假设
 *      ret = extend_gcd(b, a % b, x, y)
 *      假设该方程 b * x + (a % b) * y = gcd(a, a % b)
 *
 *      因为 gcd(a, b) == gcd(b, a % b),并且 a % b = a - (a // b) * b,
 *
 *      带入可得
 *          b * x + (a - (a // b) * b) * y = gcd(a, b)
 *      整理可得
 *          a * y + b * (x - (a // b) * y) = gcd(a, b)
 *      得到 a, b 的一组解
 *          (y, x - (a // b) * y)
 *
 *      因此是可以递归求解的,我们可以从递归终点,一点点向上推导。
 *      
 */
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <cstdio>

using namespace std;
typedef long long LL;
int n;

LL extend_gcd(LL a, LL b, LL &x, LL &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    } else {
        LL ret = extend_gcd(b, a % b, y, x);
        y -= x * (a / b);
        return ret;
    }
}

int main()
{
    LL a, b, x, y, greatest_common_divisor;
    scanf("%d", &n);
    while (n -- ) {
        scanf("%lld%lld", &a, &b);
        greatest_common_divisor = extend_gcd(a, b, x, y);
        printf("%lld %lld\n", x, y);
    }

    return 0;
}


线性同余方程

c++

线性同余方程

/*
 * 扩展欧几里得算法 求解 线性同余方程
 *  线性同余方程组
 *      ax % m = b % m
 *  做一些等式的转化
 *      ax + my = b
 *  这就是欧几里得求解线性同于方程的做法了
 *
 *  而且 b 一定是 gcd(a, m) 的倍数,否则就不存在解。
 *
 *      ai×xi+bi×yi=gcd(ai,bi)
 *
 *  不过这里需要注意数据范围
 *      a * x + b * y = gcd(a, b)
 *      a * b - b * a  = 0
 *  -->
 *      a * (x + b) + b * (y - a) = gcd(a, b)
 *  --> 因为 a, b是大于等于 0 的,因此 我们让 x, y 保持在 0附近最好,
 *  
 *  // x * (b / greatest_common_divisor) 请注意这个结果可以再次对 m 求余数,直接挪到 y 那里去
 *  
 *  关键是最后这几个 简化的步骤,有点意思
 */
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <cstdio>

using namespace std;
typedef long long LL;
int n;


LL t1, t2, t;
LL extend_gcd(LL a, LL b, LL &x, LL &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    } else {
        LL ret = extend_gcd(b, a % b, y, x);
        y -= x * (a / b);

        t = x / b;
        x = x - t * b;
        y = y + t * a;
//        printf("%lld * %lld + %lld * %lld = %lld, -->%lld\n", a, x, b, y, ret, a * x + b * y);
        return ret;
    }
}

int main()
{
    LL a, b, x, y, m, greatest_common_divisor;
    scanf("%d", &n);
    while (n -- ) {
        scanf("%lld%lld%lld", &a, &b, &m);
        greatest_common_divisor = extend_gcd(a, m, x, y);

//        printf("a=%lld, m=%lld, gcd(a, m)=(%lld), b=%lld, x=%lld, y=%lld\n", a, m , greatest_common_divisor, b, x, y);
        if (b % greatest_common_divisor == 0) {
            // x * (b / greatest_common_divisor) 请注意这个结果可以再次对 m 求余数,直接挪到 y 那里去
            printf("%lld\n", x * (b / greatest_common_divisor) % m);
        } else {
            printf("impossible\n");
        }

    }

    return 0;
}

posted @ 2022-06-30 20:34  lucky_light  阅读(93)  评论(0)    收藏  举报