我正在尝试从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位数。
约束:
输入:第一行包含一个整数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;
}
答案 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;
}