Quanto velocemente possiamo trovare tutte le combinazioni di Four Square che si sommano a N?


12

È stata posta una domanda a Stack Overflow ( qui ):

Dato un intero , stampare tutte le possibili combinazioni di valori interi di A , B , C e D che risolvono l'equazione A 2 + B 2 + C 2 + D 2 = N .NA,B,CDA2+B2+C2+D2=N

Questa domanda è ovviamente collegata alla congettura di Bachet nella teoria dei numeri (a volte chiamata teorema dei quattro quadrati di Lagrange a causa della sua dimostrazione). Ci sono alcuni articoli che discutono su come trovare una singola soluzione, ma non sono stato in grado di trovare nulla che parli di quanto velocemente possiamo trovare tutte le soluzioni per una particolare (cioè tutte le combinazioni , non tutte le permutazioni ).N

Ci ho pensato un bel po 'e mi sembra che possa essere risolto in tempo e spazio, dove N è la somma desiderata. Tuttavia, in mancanza di informazioni preliminari sull'argomento, non sono sicuro che si tratti di un reclamo significativo da parte mia o solo di un risultato banale, ovvio o già noto.O(N)N

Quindi, la domanda allora è: quanto velocemente possiamo trovare tutte le somme quadrate per una data ?N


OK, ecco l'algoritmo (quasi) O (N) a cui stavo pensando. Prime due funzioni di supporto, una funzione di radice quadrata intera più vicina:

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

E una funzione per restituire tutte le coppie TwoSquare che sommano da 0 a 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;
    }

Infine, l'algoritmo stesso:

    // 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;
    }

Come ho detto prima, dovrebbe essere abbastanza vicino a O (N), tuttavia, come sottolinea Yuval Filmus, poiché il numero di soluzioni di Four Square per N può essere di ordine (N ln ln N), quindi questo algoritmo non potrebbe essere meno di quello.


Sì, per favore pubblicalo. Sto ancora sviluppando i dettagli dell'algoritmo lineare, ma sono abbastanza sicuro che sia valido.
RBarryYoung,

5
Ω(NloglogN)O(N)

1
i=0N/2|hiListNi||loListi|

Sì, è corretto, tuttavia la tua formula è un po 'spenta perché prima sposto da 0 a apprx. N PI / 8, e in secondo luogo solo una frazione dei valori di i soddisfano hiList (Ni) + loList (i) = N, quindi non sono tutti aggiunti. In ogni caso, non c'è modo di risolvere questo problema e sono carino sicuro che questo dia la minima complessità possibile di O (N log (log (N))).
RBarryYoung,

Ma possiamo avere un algoritmo che gira in O (max (N, "numero di soluzioni")), prendendo O (n) spazio.
gnasher729,

Risposte:


15

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

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

N


Hmm, la cosa del meet-in-the-middle sembra molto simile a ciò su cui sto lavorando (quasi fatto) che è un algoritmo Merge-Match crescente / decrescente sulle coppie TwoSquare. Suona lo stesso?
RBarryYoung,

1
Probabilmente è lo stesso, meet-in-the-middle è un euristico così comune che deve avere molti nomi diversi.
Yuval Filmus,

σ(N)

σ(N)ο(N)

1
La somma dei divisori funziona davvero.
Yuval Filmus,

5

o(N2)A,B,C,DNO(N2)

O(log2n)O(log2nloglogn)


[1] MO Rabin, JO Shallit, algoritmi randomizzati in teoria dei numeri , comunicazioni sulla matematica pura e applicata 39 (1986), n. S1, pagg. S239 – S256 .


Per un algoritmo banale, hai solo bisogno di loop per A, B e C, quindi calcola D e controlla che sia un numero intero. Se hai bisogno di A ≤ B ≤ C ≤ D dovresti ottenere O (N ^ 1,5) con una costante piuttosto piccola.
gnasher729,

Circa 0,04 N ^ 1,5 triple (A, B, C) e verificare che N - A ^ 2 - B ^ 2 - C ^ 2 sia un quadrato può essere fatto molto rapidamente.
gnasher729,

-2

8ddn


1
E come risponde alla domanda? Il compito è dare tutti questi quadrupli!
Raffaello

1
Questo è già menzionato nella mia risposta.
Yuval Filmus,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.