【问题标题】:Modular exponentiation function returns the wrong int value模幂函数返回错误的 int 值
【发布时间】:2015-01-15 17:07:42
【问题描述】:

我在 C 中得到了一个模幂函数,看起来像这样。

int modexp(int m, int e, int n)
{
    printf("%i\n", m);
    printf("%i\n", e);
    printf("%i\n", n);
    printf("\n");

    if(e == 0)
    {
        return 1;
    }
    if(e%2 == 1)
    {
        return modexp(m, e-1, n) * m%n;
    }
    else
    {
        int modTemp = modexp(m, (int)(e/2), n);
        modTemp = modTemp * modTemp;
        return modTemp % n;
    }
}

我在我的main() 函数中调用它

int p = 3511;
printf("%i\n", modexp(2, p-1, p*p));

当打印 m、e 和 n 的值时,我最终得到了正确的递归值,直到 e = 0。这是函数应该返回 1 的时候。它肯定会在代码中的那个位置返回,但是我得到的不是预期的整数 1,而是 -6593454,我不知道为什么。

完整的代码可以在这里找到: https://gist.github.com/anonymous/7024ac77b2432a381968

任何意见都非常感谢......

【问题讨论】:

  • 这只是一个简单的整数溢出情况。将#include <inttypes.h> 添加到代码顶部,并将int 更改为uint64_t 并将%i 更改为%llu

标签: c return return-value integer-overflow exponentiation


【解决方案1】:

将 n 位值与 m 位值相乘会产生 (n+m) 位结果。这就是modexp(m, e-1, n) * mmodTemp * modTemp 溢出的原因。您需要扩大乘法才能从 32 位值中获得完整的 64 位结果

return ((int64_t)modexp(m, e-1, n) * m) % n;
...
int64_t modTemp = modexp(m, (int)(e/2), n);
modTemp = modTemp * modTemp;
return modTemp % n;

【讨论】:

    【解决方案2】:

    e == 0 时确实返回了 1,但这个返回值是在以前的递归级别上进行的计算中的一项。如果你像这样修改你的代码:

    #include <stdio.h>
    int modexp(int m, int e, int n)
    {
        printf("%i\n", m);
        printf("%i\n", e);
        printf("%i\n", n);
        printf("\n");
    
        if(e == 0)
        {
            printf("returning 1\n");
            return 1;
        }
        if(e%2 == 1)
        {
            int tmp=modexp(m, e-1, n) * m%n;
            printf("returning %d\n",tmp);
            return tmp; 
        }
        else
        {
            int modTemp = modexp(m, (int)(e/2), n);
            modTemp = modTemp * modTemp;
            modTemp= modTemp % n;
            printf("returning %d\n",modTemp);
            return modTemp;;
        }
    }
    
    
    int main(){
        int p = 3511;
        printf("%d\n", modexp(2, p-1, p*p));
        return 0;
    }
    

    最后你会得到这个输出:

    returning 1
    returning 2
    returning 4
    returning 8
    returning 64
    returning 4096
    returning 8192
    returning 5473259
    returning 10946518
    returning 2217782
    returning 5855618
    returning 11711236
    returning 8916378
    returning 5505635
    returning -1026672
    returning 975519
    returning 1951038
    returning 195330
    returning 390660
    returning -6593454
    -6593454
    

    这表明为e == 0 返回的 1 用于进一步计算。

    在某些时候你的代码会溢出int,这就是为什么你最后会得到一个负数。您可能应该使用更长的类型。

    【讨论】:

      【解决方案3】:

      我同意这是 modTemp 变量的溢出。但是,为了清楚起见,我建议仅将 modTemp 的类型从 Int 更改为 long,并将 m/e/n 更改为 b/e/m,如下所示:

      #include <stdio.h>
      
      int modexp(int b, int e, int m); // b: base; e: exp; m: modulus
      
      
      int main()
      {
          int p = 3511; // given number p
          printf("%i\n", modexp(2, p-1, p*p)); // last line printed is the remainder of mod exp
      }
      
      
      int modexp(int b, int e, int m)
      {
          printf("%i\n", b);
          printf("%i\n", e);
          printf("%i\n", m);
          printf("\n");
      
          if(e == 0)
          {
              return 1;
          }
          if(e%2 == 1)
          {
              return modexp(b, e-1, m) * b%m;
          }
          else
          {
              long modTemp = modexp(b, (int)(e/2), m); // !!here modTemp is declared as long!!
              modTemp = modTemp * modTemp;
              return modTemp % m;
          }
      }
      

      【讨论】:

        【解决方案4】:

        您的号码已满。它们在这里很可能是 32 位或 64 位有符号整数,这意味着最大大小可以是 231=2147483648 或 263 = 9223372036854775807。看看大小modexp(...) 乘以 m%n。这是一个相当大的数字:)

        modTemp * modTemp 也是如此

        【讨论】:

        • 2⁶³ = 9223372036854775808,不是 9223372036854775807。但实际限制是 2⁶³ - 12³¹ - 1
        猜你喜欢
        • 2021-07-17
        • 2020-07-08
        • 2022-01-23
        • 2016-03-26
        • 2015-12-28
        • 1970-01-01
        • 1970-01-01
        • 2013-03-22
        • 1970-01-01
        相关资源
        最近更新 更多