天真的蛮力是这样的:
n = 3200724;
lim = sqrt (n) + 1;
for (a = 0; a <= lim; a++)
for (b = 0; b <= lim; b++)
for (c = 0; c <= lim; c++)
for (d = 0; d <= lim; d++)
if (a * a + b * b + c * c + d * d == n)
printf ("%d %d %d %d\n", a, b, c, d);
不幸的是,这将导致超过一万亿次循环,效率并不高。
实际上,您可以通过在每个级别上排除大量不可能的情况来做得更好,例如:
#include <stdio.h>
int main(int argc, char *argv[]) {
int n = atoi (argv[1]);
int a, b, c, d, na, nb, nc, nd;
int count = 0;
for (a = 0, na = n; a * a <= na; a++) {
for (b = 0, nb = na - a * a; b * b <= nb; b++) {
for (c = 0, nc = nb - b * b; c * c <= nc; c++) {
for (d = 0, nd = nc - c * c; d * d <= nd; d++) {
if (d * d == nd) {
printf ("%d %d %d %d\n", a, b, c, d);
count++;
}
tot++;
}
}
}
}
printf ("Found %d solutions\n", count);
return 0;
}
它仍然是蛮力,但没有那么粗暴,因为它知道何时尽早停止每个级别的循环。
在我(相对)适度的盒子上,不到一秒钟 (a) 即可获得最多 50,000 个数字的所有解决方案。除此之外,它开始需要更多时间:
n time taken
---------- ----------
100,000 3.7s
1,000,000 6m, 18.7s
对于n = ten million,在我杀死它之前已经过了大约一个半小时。
所以,我想说蛮力在某种程度上是完全可以接受的。除此之外,还需要更多的数学解决方案。
为了提高效率,您只能检查d >= c >= b >= a 的那些解决方案。这是因为您可以将这些组合中的所有解决方案构建成排列(可能会删除重复项,其中两个或多个 a、b、c 或 d 的值相同)。
此外,d 循环的主体不需要检查d 的每个值,只需检查最后一个可能的值。
在这种情况下,获取 1,000,000 的结果需要不到 10 秒而不是超过 6 分钟:
0 0 0 1000
0 0 280 960
0 0 352 936
0 0 600 800
0 24 640 768
: : : :
424 512 512 544
428 460 500 596
432 440 480 624
436 476 532 548
444 468 468 604
448 464 520 560
452 452 476 604
452 484 484 572
500 500 500 500
Found 1302 solutions
real 0m9.517s
user 0m9.505s
sys 0m0.012s
代码如下:
#include <stdio.h>
int main(int argc, char *argv[]) {
int n = atoi (argv[1]);
int a, b, c, d, na, nb, nc, nd;
int count = 0;
for (a = 0, na = n; a * a <= na; a++) {
for (b = a, nb = na - a * a; b * b <= nb; b++) {
for (c = b, nc = nb - b * b; c * c <= nc; c++) {
for (d = c, nd = nc - c * c; d * d < nd; d++);
if (d * d == nd) {
printf ("%4d %4d %4d %4d\n", a, b, c, d);
count++;
}
}
}
}
printf ("Found %d solutions\n", count);
return 0;
}
而且,根据 DSM 的建议,d 循环可以完全消失(因为只有一个可能的值 d(扣除负数)并且可以计算出来),从而减少了一百万个案例对我来说是 2 秒,1000 万个案例是更易于管理的 68 秒。
那个版本如下:
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[]) {
int n = atoi (argv[1]);
int a, b, c, d, na, nb, nc, nd;
int count = 0;
for (a = 0, na = n; a * a <= na; a++) {
for (b = a, nb = na - a * a; b * b <= nb; b++) {
for (c = b, nc = nb - b * b; c * c <= nc; c++) {
nd = nc - c * c;
d = sqrt (nd);
if (d * d == nd) {
printf ("%d %d %d %d\n", a, b, c, d);
count++;
}
}
}
}
printf ("Found %d solutions\n", count);
return 0;
}
(a):所有的计时都是在注释掉内部printf 的情况下完成的,这样I/O 就不会扭曲数字。