How to find all possible values of four variables when squared sum to N?

后端 未结 4 479
醉酒成梦
醉酒成梦 2020-12-28 23:39

A^2+B^2+C^2+D^2 = N Given an integer N, print out all possible combinations of integer values of ABCD which solve the equation.

<
4条回答
  •  自闭症患者
    2020-12-29 00:13

    Naive brute force would be something like:

    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);
    

    Unfortunately, this will result in over a trillion loops, not overly efficient.

    You can actually do substantially better than that by discounting huge numbers of impossibilities at each level, with something like:

    #include 
    
    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;
    }
    

    It's still brute force, but not quite as brutish inasmuch as it understands when to stop each level of looping as early as possible.

    On my (relatively) modest box, that takes under a second (a) to get all solutions for numbers up to 50,000. Beyond that, it starts taking more time:

         n          time taken
    ----------      ----------
       100,000            3.7s
     1,000,000       6m, 18.7s
    

    For n = ten million, it had been going about an hour and a half before I killed it.

    So, I would say brute force is perfectly acceptable up to a point. Beyond that, more mathematical solutions would be needed.


    For even more efficiency, you could only check those solutions where d >= c >= b >= a. That's because you could then build up all the solutions from those combinations into permutations (with potential duplicate removal where the values of two or more of a, b, c, or d are identical).

    In addition, the body of the d loop doesn't need to check every value of d, just the last possible one.

    Getting the results for 1,000,000 in that case takes under ten seconds rather than over six minutes:

       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
    

    That code follows:

    #include 
    
    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;
    }
    

    And, as per a suggestion by DSM, the d loop can disappear altogether (since there's only one possible value of d (discounting negative numbers) and it can be calculated), which brings the one million case down to two seconds for me, and the ten million case to a far more manageable 68 seconds.

    That version is as follows:

    #include 
    #include 
    
    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): All timings are done with the inner printf commented out so that I/O doesn't skew the figures.

提交回复
热议问题