Seberapa cepat kita dapat menemukan semua kombinasi Four-Square yang berjumlah N?

12

Sebuah pertanyaan diajukan di Stack Overflow (di sini ):

Dengan bilangan bulat , cetak semua kemungkinan kombinasi nilai integer A , B , C dan D yang menyelesaikan persamaan A 2 + B 2 + C 2 + D 2NA,B,CD .A2+B2+C2+D2=N

Pertanyaan ini tentu saja terkait dengan dugaan Bachet dalam teori bilangan (kadang-kadang disebut Lagrange's Four Square Theorem karena buktinya). Ada beberapa makalah yang membahas bagaimana menemukan solusi tunggal, tetapi saya tidak dapat menemukan apa pun yang berbicara tentang seberapa cepat kita dapat menemukan semua solusi untuk suatu solusi tertentu.(yaitu, semuakombinasi, tidak semuapermutasi).N

Saya telah memikirkannya sedikit dan menurut saya hal itu dapat diselesaikan dalam ruang dan waktu , di mana N adalah jumlah yang diinginkan. Namun, karena tidak ada informasi sebelumnya tentang masalah ini, saya tidak yakin apakah itu merupakan klaim signifikan pada bagian saya atau hanya hasil yang sepele, jelas atau sudah diketahui.O(N)N

Jadi, pertanyaannya adalah, seberapa cepat kita dapat menemukan semua Jumlah Empat-Kuadrat untuk diberikan ?N


OK, inilah algoritma (hampir) O (N) yang saya pikirkan. Dua fungsi pendukung pertama, fungsi akar kuadrat integer terdekat:

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

Dan fungsi untuk mengembalikan semua pasangan TwoSquare yang menjumlahkan dari 0 ke 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;
    }

Akhirnya, algoritma itu sendiri:

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

Seperti yang saya katakan sebelumnya, itu harus cukup dekat dengan O (N), namun, seperti yang Yuval Filmus tunjukkan, karena jumlah solusi Four Square untuk N dapat menjadi urutan (N ln ln N), maka algoritma ini tidak dapat kurang dari itu.

RBarryYoung
sumber
Ya, silakan kirim. Saya masih mengembangkan detail algoritma linier, tapi saya cukup yakin itu valid.
RBarryYoung
5
Ω(NloglogN)O(N)
1
i=0N/2|hiListNi||loListi|
Ya, itu benar, namun rumus Anda sedikit tidak aktif karena pertama saya berkisar dari 0 hingga apprx. N PI / 8, dan yang kedua hanya sebagian kecil dari nilai i memuaskan hiList (Ni) + loList (i) = N, jadi mereka tidak semua ditambahkan. Dalam hal apa pun, tidak ada cara untuk memperbaikinya dan saya cukup yakin bahwa ini memberikan kompleksitas minimum yang mungkin dari O (N log (log (N))).
RBarryYoung
Tetapi kita dapat memiliki algoritma yang berjalan di O (maks (N, "jumlah solusi")), mengambil ruang O (n).
gnasher729

Jawaban:

15

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

Ω(NloglogN)N8σ(N)σ(N)(eγϵ)NloglogN teorema Grönwall ).

N

Yuval Filmus
sumber
Hmm, hal meet-in-the-middle terdengar sangat mirip dengan apa yang saya kerjakan (hampir selesai) yang merupakan algoritma Merge-Match yang naik / turun pada pasangan TwoSquare. Apakah itu terdengar sama?
RBarryYoung
1
Itu mungkin sama, meet-in-the-middle adalah heuristik yang umum sehingga harus memiliki banyak nama berbeda.
Yuval Filmus
σ(N)
σ(N)ο(N)
1
Jumlah fungsi pembagi memang.
Yuval Filmus
5

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

O(log2n)O(log2nloglogn)


[1] MO Rabin, JO Shallit, Algoritma Acak dalam Teori Bilangan , Komunikasi tentang Matematika Murni dan Terapan 39 (1986), no. S1, hal. S239 – S256 .

Juho
sumber
Untuk algoritma sepele, Anda hanya perlu loop untuk A, B, dan C dan kemudian menghitung D dan periksa itu bilangan bulat. Jika Anda memerlukan A ≤ B ≤ C ≤ D Anda harus mendapatkan O (N ^ 1.5) dengan konstanta yang agak kecil.
gnasher729
Sekitar 0,04 N ^ 1,5 tiga kali lipat (A, B, C), dan memeriksa bahwa N - A ^ 2 - B ^ 2 - C ^ 2 adalah persegi dapat dilakukan dengan sangat cepat.
gnasher729
-2

8ddn

tamu
sumber
1
Dan bagaimana ini menjawab pertanyaan? Tugasnya adalah memberikan semua empat kali lipat ini!
Raphael
1
Ini sudah disebutkan dalam jawaban saya.
Yuval Filmus