【问题标题】:Modular multiplicative inverse模乘逆
【发布时间】:2017-08-06 19:10:54
【问题描述】:

我正在计算 ((A^B)/C)%M,但是当 A、B、C、M 的数量很大时,我的代码不起作用。此代码在 A、B、C、D 较小时给出正确答案int

这里有什么问题?

这里 C 和 M 互质

示例输入 2 3 4 5 示例输出 2

这些输入的代码失败 969109092 60139073 122541116 75884463

C 程序

#include <stdio.h>

int d,x,y;

模指数 (A^B)%M

int power(int A, int B, int M)
{
    long long int result=1;
    while(B>0)
    {
        if(B % 2 ==1)
        {
            result=(result * A)%M;
        }
        A=(A*A)%M;
        B=B/2;
    }
    return result;
}

模乘逆

void extendedEuclid(int A, int B)
{
    if(B == 0)
    {
        d = A;
        x = 1;
        y = 0;
    }
    else
    {
        extendedEuclid(B,A%B);
        int temp = x;
        x = y;
        y = temp - (A/B)*y;
    }
}

int modInv(int A, int M)
{
    extendedEuclid(A,M);
    return (x%M+M)%M;
}

ma​​in()

int main()
{
    int A,B,C,M;
    scanf("%d %d %d %d",&A,&B,&C,&M);
    int inv = modInv(C,M)%M;
    printf("%d\n",inv);
    long long int p = (power(A,B,M))%M;
    printf("%d\n",p);
    long long int ans = (p * inv)%M;
    //printf("%d",((modInv(C,M)*(power(A,B,M))))%M);
    printf("%lld",ans);
    return 0;
}

【问题讨论】:

  • 您可能超出了整数所能容纳的范围。请改用 long 或 long long。
  • 我更改了这些值 long long int result=1; long long int p = (power(A,B,M))%M; long long int ans = (p * inv)%M;但仍然无法正常工作
  • 我不确定逆函数的实现。尝试使用类似 wiki extended Euclid algorithm 的东西。像 (t, newt) = ... 这样具有双重赋值的代码需要替换为使用临时变量的代码。
  • 我没有遵循代码但是如果你想要一个涉及常数除数D的表达式的模数% M,那么你应该在计算过程中使用模数% (M * D),最后使用% M 取适当的模数。
  • 数字大是什么意思?举个例子! (在问题中)当算法失败时你会尝试哪些数字?

标签: c number-theory


【解决方案1】:

在 cmets 中注明修复的测试版本:

#include <stdio.h>

int d,x,y;

int power(int A, int B, int M)
{
    long long int result=1;
    long long int S = A;            /* fix */
    while(B>0)
    {
        if(B % 2 ==1)
        {
            result=(result * S)%M;  /* fix */
        }
        S=(S*S)%M;                  /* fix */
        B=B/2;
    }
    return (int)result;
}

void extendedEuclid(int A, int B)
{
int temp;                           /* C */
    if(B == 0)
    {
        d = A;
        x = 1;
        y = 0;
    }
    else
    {
        extendedEuclid(B,A%B);
        temp = x;
        x = y;
        y = temp - (A/B)*y;
    }
}

int modInv(int A, int M)
{
    extendedEuclid(A,M);
/*  x = x%M;                        ** not needed */
    if (x < 0)                      /* fix */
        x += M;                     /* fix */
    return (x);                     /* fix */
}

int main()
{
    int A,B,C,M;                    /* C */
    int inv, p, ans;                /* C */
    A = 969109092;                  /* 2^2 × 3^2 ×7 × 1249 × 3079 */
    B =  60139073;                  /* 60139073 */
    C = 122541116;                  /* 2^2 × 1621 × 18899 */
    M =  75884463;                  /* 3^2 × 8431607 */

    inv = modInv(C,M)%M;            /* 15543920 */
    printf("%d\n",inv);
    p = power(A,B,M)%M;             /*  6704397 */
    printf("%d\n",p);
    ans = (unsigned)(((unsigned long long)p * inv)%M);  /* fix 22271562 */
    printf("%d\n",ans);
    return 0;
}

【讨论】:

  • 感谢这段代码帮助我理解我的错误
  • @VikasGautam - 我对 modInv() 进行了更改。不需要 x%M,扩展的 Euclid 算法已经实现了这一点,只是结果可能是否定的。完成后需要检查 x x < 0、x += M,如固定代码所示。
【解决方案2】:

代码至少有以下问题:

intA*A 中溢出。代码需要使用更广泛的数学计算乘积 A*A。这就是为什么代码适用于小值而不是大值的原因。

// A=(A*A)%M;
A = ((long long)A*A) % M;
// or 
A = (1LL*A*A) % M;

错误的打印说明符。这意味着编译器警告未完全启用。节省时间,全部启用。

long long int p = (power(A,B,M))%M;
// printf("%d\n",p);
printf("%lld\n",p);

代码有问题,有负值。与其修补 int 漏洞,不如使用 unsigned 类型。

unsigned power(unsigned A, unsigned B, unsigned M) {
  unsigned long long result = 1;
  ...

power(A,0,1) 中的角落案例失败。当M==1 时,result 应该为 0。

// long long int result=1;
long long int result=1%M;

【讨论】:

    【解决方案3】:

    int的值可能不够大,试试long,或者double。

    小心,因为 power 返回的 int 不是 long long int

    【讨论】:

    • "小心,因为 power 返回一个 int 不是 long long int" 不清楚,因为在给定 % by int M 的情况下,结果应该始终在 int 范围内。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-10
    相关资源
    最近更新 更多