Jak szybko możemy znaleźć wszystkie kombinacje czterech kwadratów, które sumują się do N?


12

Pytanie zostało zadane w Stack Overflow ( tutaj ):

Biorąc pod uwagę całkowitą wydrukować wszystkie możliwe kombinacje wartości całkowitych z i dzięki którym rozwiązuje się równanie .N.ZA,b,doreZA2)+b2)+do2)+re2)=N.

To pytanie jest oczywiście związane z hipotezą Bacheta w teorii liczb (czasami nazywaną twierdzeniem Four Square Lagrange'a z powodu jego dowodu). Istnieje kilka artykułów, które dyskutują o tym, jak znaleźć pojedyncze rozwiązanie, ale nie byłem w stanie znaleźć niczego, co mówi o tym, jak szybko możemy znaleźć wszystkie rozwiązania dla konkretnego (to znaczy wszystkie kombinacje , nie wszystkie permutacje ).N.

Długo o tym myślałem i wydaje mi się, że można to rozwiązać w czasie i przestrzeni , gdzie jest pożądaną sumą. Jednak bez wcześniejszych informacji na ten temat nie jestem pewien, czy jest to znaczące roszczenie z mojej strony, czy tylko trywialny, oczywisty lub już znany wynik.O(N.)N.

Pytanie zatem brzmi: jak szybko możemy znaleźć wszystkie Kwadraty Czterech Kwadratów dla danego ?N.


OK, oto algorytm (prawie) O (N), o którym myślałem. Dwie pierwsze funkcje pomocnicze, funkcja najbliższego pierwiastka z liczby całkowitej:

    // the nearest integer whose square is less than or equal to N
    public int SquRt(int N)
    {
        return (int)Math.Sqrt((double)N);
    }

I funkcja zwracająca wszystkie pary TwoSquare sumujące od 0 do N:

    // Returns a list of all sums of two squares less than or equal to N, in order.
    public List<List<int[]>> TwoSquareSumsLessThan(int N)
    {
        //Make the index array
        List<int[]>[] Sum2Sqs = new List<int[]>[N + 1];

        //get the base square root, which is the maximum possible root value
        int baseRt = SquRt(N);

        for (int i = baseRt; i >= 0; i--)
        {
            for (int j = 0; j <= i; j++)
            {
                int sum = (i * i) + (j * j);
                if (sum > N)
                {
                    break;
                }
                else
                {
                    //make the new pair
                    int[] sumPair = { i, j };
                    //get the sumList entry
                    List<int[]> sumLst;
                    if (Sum2Sqs[sum] == null)
                    {   
                        // make it if we need to
                        sumLst = new List<int[]>();
                        Sum2Sqs[sum] = sumLst;
                    }
                    else
                    {
                        sumLst = Sum2Sqs[sum];
                    }
                    // add the pair to the correct list
                    sumLst.Add(sumPair);
                }
            }
        }

        //collapse the index array down to a sequential list
        List<List<int[]>> result = new List<List<int[]>>();
        for (int nn = 0; nn <= N; nn++)
        {
            if (Sum2Sqs[nn] != null) result.Add(Sum2Sqs[nn]);
        }

        return result;
    }

Wreszcie sam algorytm:

    // Return a list of all integer quads (a,b,c,d), where:
    //      a^2 + b^2 + c^2 + d^2 = N,
    // and  a >= b >= c >= d,
    // and  a,b,c,d >= 0
    public List<int[]> FindAllFourSquares(int N)
    {
        // get all two-square sums <= N, in descending order
        List<List<int[]>> Sqr2s = TwoSquareSumsLessThan(N);

        // Cross the descending list of two-square sums <= N with
        // the same list in ascending order, using a Merge-Match
        // algorithm to find all combinations of pairs of two-square
        // sums that add up to N
        List<int[]> hiList, loList;
        int[] hp, lp;
        int hiSum, loSum;
        List<int[]> results = new List<int[]>();
        int prevHi = -1;
        int prevLo = -1;

        //  Set the Merge sources to the highest and lowest entries in the list
        int hi = Sqr2s.Count - 1;
        int lo = 0;

        //  Merge until done ..
        while (hi >= lo)
        {
            // check to see if the points have moved
            if (hi != prevHi)
            {
                hiList = Sqr2s[hi];
                hp = hiList[0];     // these lists cannot be empty
                hiSum = hp[0] * hp[0] + hp[1] * hp[1];
                prevHi = hi;
            }
            if (lo != prevLo)
            {
                loList = Sqr2s[lo];
                lp = loList[0];     // these lists cannot be empty
                loSum = lp[0] * lp[0] + lp[1] * lp[1];
                prevLo = lo;
            }

            // do the two entries' sums together add up to N?
            if (hiSum + loSum == N)
            {
                // they add up, so cross the two sum-lists over each other
                foreach (int[] hiPair in hiList)
                {
                    foreach (int[] loPair in loList)
                    {
                        // make a new 4-tuple and fill it
                        int[] quad = new int[4];
                        quad[0] = hiPair[0];
                        quad[1] = hiPair[1];
                        quad[2] = loPair[0];
                        quad[3] = loPair[1];

                        // only keep those cases where the tuple is already sorted
                        //(otherwise it's a duplicate entry)
                        if (quad[1] >= quad[2]) //(only need to check this one case, the others are implicit)
                        {
                            results.Add(quad);
                        }
                        //(there's a special case where all values of the 4-tuple are equal
                        // that should be handled to prevent duplicate entries, but I'm
                        // skipping it for now)
                    }
                }
                // both the HI and LO points must be moved after a Match
                hi--;
                lo++;
            }
            else if (hiSum + loSum < N)
            {
                lo++;   // too low, so must increase the LO point
            }
            else    // must be > N
            {
                hi--;   // too high, so must decrease the HI point
            }
        }
        return results;
    }

Jak powiedziałem wcześniej, powinno być dość blisko O (N), jak jednak zauważa Yuval Filmus, ponieważ liczba rozwiązań Four Square dla N może być uporządkowana (Nnnn N), to algorytm nie może być mniej niż to.


Tak, opublikuj to. Nadal rozwijam szczegóły algorytmu liniowego, ale jestem prawie pewien, że jest poprawny.
RBarryYoung

5
Dla przypomnienia, wydaje się, że czasami jest tyle rozwiązań , więc tak naprawdę nie możemy mieć algorytmu . O ( N )Ω(N.loglogN.)O(N.)
Yuval Filmus,

1
Stąd wygląda na to, że catch (i dodatkowy czynnik nieliniowy) pochodzi z dwóch pętli foreach () w głównej pętli while; twój całkowity czas to w zasadzie, a problemem jest to, że rozmiary hiList i loList niekoniecznie są ograniczone żadną stałą. i=0N/2|hiListNi||loListi|
Steven Stadnicki,

Tak, to prawda, ale twoja formuła jest trochę nieaktualna, ponieważ najpierw i waha się od 0 do apprx. N PI / 8, a po drugie tylko ułamek wartości i spełniają hiList (Ni) + loList (i) = N, więc nie są one wszystkie dodane. W każdym razie nie ma sposobu, aby to naprawić i jestem ładna upewnij się, że daje to minimalną możliwą złożoność O (N log (log (N))).
RBarryYoung

Ale możemy mieć algorytm działający w O (max (N, „liczba rozwiązań”)), zajmujący przestrzeń O (n).
gnasher729

Odpowiedzi:


15

O(N)A,BNM=A2+B2N(A,B)TNMM,NMT

Ω(NloglogN)N8σ(N)σ(N)(eγϵ)NloglogN

N


Hmm, spotkanie w środku wydaje się bardzo podobne do tego, nad czym pracuję (prawie zrobiłem), który jest rosnącym / malejącym algorytmem scalania dopasowania w parach TwoSquare. Czy to brzmi tak samo?
RBarryYoung

1
Prawdopodobnie to samo, spotkanie w środku jest tak powszechną heurystyką, że musi mieć wiele różnych nazw.
Yuval Filmus,

σ(N)

σ(N.)ο(N.)

1
Suma dzielników rzeczywiście działa.
Yuval Filmus

5

o(N2)ZA,b,do,reN.O(N.2))

O(log2)n)O(log2)nloglogn)


[1] MO Rabin, JO Shallit, Randomized Algorytmy w teorii liczb , Communications on Pure and Applied Mathematics 39 (1986), no. S1, ss. S239 – S256 .


W przypadku trywialnego algorytmu potrzebujesz tylko pętli dla A, B i C, a następnie oblicz D i sprawdź, czy jest to liczba całkowita. Jeśli potrzebujesz A ≤ B ≤ C ≤ D, powinieneś otrzymać O (N ^ 1,5) z raczej małą stałą.
gnasher729

Około 0,04 N ^ 1,5 trzykrotnie (A, B, C), a sprawdzenie, że N - A ^ 2 - B ^ 2 - C ^ 2 jest kwadratem, można zrobić bardzo szybko.
gnasher729

-2

8reren


1
Jak to odpowiada na pytanie? Zadaniem jest dać te wszystkie poczwórne!
Raphael

1
Jest to już wspomniane w mojej odpowiedzi.
Yuval Filmus,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.