项目Euler +#97模块化指数不起作用

时间:2017-06-19 23:06:37

标签: c modular-arithmetic

我正在尝试从Hackerrank解决Project Euler+ #97。问题要求计算A x B ** C + D的最后12位数。我的尝试是使用来自Wikipedia的模幂运算模10 ** 12,以便有效地计算最后12位数并避免溢出。但是,除了样本2 x 3 ** 4 + 5之外的所有情况我都错了。根据约束,unsigned long long应该没有溢出。

问题:

现在我们想学习如何计算这些大数字的最后几位数。我们假设我们有很多数字A x B ** C + D,我们想知道这些数字的最后12位数。

约束

  • 1≤T≤500000
  • 1≤A,B,C,D≤10** 9

输入:第一行包含一个整数T - 测试次数。 T行跟随包含4个整数(A,B,C和D)。

输出:输出恰好包含12位数的一行 - 所有结果总和的最后12位数。如果总和小于10 ** 12则打印相应的前导零数。

我在C中的尝试

#include <stdio.h>

int main() {
    const unsigned long long limit = 1000000000000;
    int cases;
    for (scanf("%d", &cases); cases; --cases) {
        // mult = A, base = B, exp = C, add = D
        unsigned long long mult, base, exp, add;
        scanf("%llu %llu %llu %llu", &mult, &base, &exp, &add);
        base = base % limit;
        while (exp) {
            if (exp & 1) {
               mult = (mult * base) % limit;
            }
            exp >>= 1;
            base = (base * base) % limit;
        }
        printf("%012llu\n", (mult + add) % limit);
    }
    return 0;
}

1 个答案:

答案 0 :(得分:2)

我认为你可以溢出无符号长long数学(例如 - modulo 2 ^ 64),因为你的内部循环中的base的计算可以高达(10 ^ 12 - 1)^ 2~ = 10 ^ 24~ = 2 ^ 79.726,远远超过2 ^ 64。例如,考虑B = 10 ^ 6 - 1和C = 4.

在我的MacBook Pro上运行带有clang 8.1.0的64位版本的Mac OS X:

#include <stdio.h>

int main()
{
  fprintf(stdout, "sizeof(unsigned long long) = %u\n", (unsigned) sizeof(unsigned long long));
  fprintf(stdout, "sizeof(__uint128_t) = %u\n", (unsigned) sizeof(__uint128_t));
  fprintf(stdout, "sizeof(long double) = %u\n", (unsigned) sizeof(long double));

  return 0;
}

表示:

sizeof(unsigned long long) = 8
sizeof(__uint128_t) = 16
sizeof(long double) = 16

如果你的平台长达16或10,那么我认为你很清楚。如果它像我的那样说8,那么你需要重新设计你的答案,或者强制128b(或80b)整数数学,或者用其他方式模仿它。

您可以尝试使用gcc和clang支持的__uint128_t。否则,你需要求助于long double和fmodl()这样的东西,它可能有足够的尾数位,但可能无法提供你想要的确切答案。

此外,您不会像任务所说的那样积累多个结果。根据你的程序,我在这里拍摄,但是使用了__uint128_t。

#include <stdio.h>
#include <stdlib.h>

#define BILLION     1000000000
#define TRILLION 1000000000000

int main() 
{
  const __uint128_t limit = TRILLION;
  unsigned long     cases = 0;
  __uint128_t       acc   = 0;

  if (scanf("%lu", &cases) != 1 || cases == 0 || cases > 500000)
    abort();

  while (cases-- > 0)
  {            
    unsigned long a, b, c, d;
    __uint128_t b2c = 1, bbase;

    if (scanf("%lu %lu %lu %lu", &a, &b, &c, &d) != 4 ||
        a == 0 || a > BILLION || b == 0 || b > BILLION || 
        c == 0 || c > BILLION || d == 0 || d > BILLION)
      abort();

    for (bbase = b; c > 0; c >>= 1) 
    {
      if ((c & 0x1) != 0)
        b2c = (b2c * bbase) % limit;    // 64b overflow: ~10^12 * ~10^12 ~= 10^24 > 2^64

      bbase = (bbase * bbase) % limit;  // same overflow issue as above
    }

    // can do modulus on acc only once at end of program instead because
    // 5 * 10^5 * (10^9 * (10^12 - 1) + 10^9) = 5 * 10^26 < 2^128

    acc += a * b2c + d;  
  }

  acc %= limit;

  printf("%012llu\n", (unsigned long long) acc);

  return 0;
}