Як швидко ми можемо знайти всі комбінації з чотирьох квадратних, що дорівнюють N?


12

Питання було задано на "Переповнення стека" ( тут ):

Беручи під увагу ціле число , роздрукувати всі можливі комбінації цілочисельних значень A , B , C і D , які вирішують рівняння A 2 + B 2 + C 2 + D 2 = N .NA,B,CDA2+B2+C2+D2=N

Це питання, звичайно , пов'язано з гіпотезою Баша по теорії чисел (іноді званого Лагранж чотири квадратної теорему з його докази). Є деякі статті, які обговорюють, як знайти єдине рішення, але мені не вдалося знайти нічого, що б говорило про те, як швидко ми можемо знайти всі рішення для певного (тобто всі комбінації , а не всі перестановки ).N

Я про це думав зовсім небагато, і мені здається, що це можна вирішити за часу та простору, де N - бажана сума. Однак, не маючи будь-якої попередньої інформації з цього приводу, я не впевнений, чи є це вагомою претензією з мого боку або просто тривіальним, очевидним або вже відомим результатом.O(N)N

Отже, питання полягає в тому, наскільки швидко ми можемо знайти всі чотири квадратні суми для даного ?N


Гаразд, ось алгоритм (майже) O (N), про який я думав. Перші дві підтримуючі функції, найближча цілочисельна коренева функція:

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

І функція для повернення всіх пар TwoSquare, підсумовуючи від 0 до 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;
    }

Нарешті, сам алгоритм:

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

Як я вже говорив раніше, він повинен бути досить близьким до O (N), однак, як зазначає Юваль Філіус, оскільки кількість рішень чотирьох квадратних для N може бути порядком (N ln ln N), то цього алгоритму не могло бути менше, ніж це.


Так, будь ласка, опублікуйте. Я все ще розробляю деталі лінійного алгоритму, але я впевнений, що він дійсний.
RBarryYoung

5
Ω(NloglogN)O(N)

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

Так, це правильно, проте ваша формула трохи відключена, тому що спочатку я коливається від 0 до apprx. N PI / 8, а другий лише частина значень я задовольняю hiList (Ni) + loList (i) = N, тому вони не всі додані. У будь-якому випадку, немає способу це виправити, і я досить впевнений, що це дає мінімально можливу складність O (N log (log (N))).
RBarryYoung

Але у нас може бути алгоритм, який працює в O (max (N, "кількість рішень")), займаючи простір O (n).
gnasher729

Відповіді:


15

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

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

N


Хм, річ «зустрітися в середині» звучить дуже схоже на те, над чим я працюю (майже зроблено), що є алгоритмом злиття / спадання злиття-відповідності для пар TwoSquare. Це звучить однаково?
RBarryYoung

1
Це, мабуть, те саме, зустріч в середині - це така поширена евристика, що вона повинна мати багато різних імен.
Yuval Filmus

σ(N)

σ(N)ο(N)

1
Сума дільників справді функціонує.
Yuval Filmus

5

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

O(log2n)O(log2nloglogn)


[1] М.О. Рабін, Дж. О. Шалліт, Рандомізовані алгоритми в теорії чисел , Зв’язок з чистої та прикладної математики 39 (1986), вип . S1, стор. S239-S256 .


Для тривіального алгоритму вам потрібні лише петлі для A, B і C, а потім обчислити D і перевірити це ціле число. Якщо вам потрібен A ≤ B ≤ C ≤ D, ви повинні отримати O (N ^ 1,5) з досить малою константою.
gnasher729

Приблизно 0,04 N ^ 1,5 трійки (A, B, C), і перевірити, що N - A ^ 2 - B ^ 2 - C ^ 2 є квадратом, можна зробити дуже швидко.
gnasher729

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.