Làm thế nào nhanh chóng chúng ta có thể tìm thấy tất cả các kết hợp Bốn hình vuông tổng bằng N?


12

Một câu hỏi đã được hỏi tại Stack Overflow ( tại đây ):

Đưa ra một số nguyên N , in ra tất cả các kết hợp có thể của giá trị nguyên A,B,CD mà giải quyết phương trình A2+B2+C2+D2=N .

Câu hỏi này tất nhiên có liên quan đến Giả thuyết của Bachet trong lý thuyết số (đôi khi được gọi là Định lý Bốn bình phương của Lagrange vì bằng chứng của ông). Có một số bài viết thảo luận về cách tìm một giải pháp duy nhất, nhưng tôi không thể tìm thấy bất cứ điều gì nói về việc chúng ta có thể tìm thấy tất cả các giải pháp cho một cụ thể như thế nào N(nghĩa là tất cả các kết hợp , không phải tất cả các hoán vị ).

Tôi đã suy nghĩ về nó khá nhiều và dường như nó có thể được giải quyết trong thời gian và không gian O(N) , trong đó N là tổng số mong muốn. Tuy nhiên, thiếu bất kỳ thông tin trước về chủ đề này, tôi không chắc đó là một yêu cầu quan trọng về phía tôi hay chỉ là một kết quả tầm thường, rõ ràng hoặc đã được biết đến.

Vì vậy, câu hỏi sau đó là, chúng ta có thể tìm thấy tất cả các Tứ giác vuông cho một cho trước nhanh đến mức Nnào?


OK, đây là thuật toán (gần) O (N) mà tôi đã nghĩ đến. Hai hàm hỗ trợ đầu tiên, một hàm căn bậc hai gần nhất:

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

Và một hàm để trả về tất cả các cặp TwoSapes tổng từ 0 đến 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;
    }

Cuối cùng, thuật toán chính nó:

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

Như tôi đã nói trước đây, nó khá gần với O (N), tuy nhiên, như Yuval Filmus chỉ ra, vì số lượng giải pháp Four Square cho N có thể theo thứ tự (N ln ln N), nên thuật toán này không thể ít hơn.


Vâng, xin vui lòng gửi nó. Tôi vẫn đang phát triển các chi tiết thuật toán tuyến tính, nhưng tôi khá chắc chắn rằng nó hợp lệ.
RBarryYoung

5
Đối với bản ghi, có vẻ như đôi khi có nhiều giải pháp , vì vậy chúng ta thực sự không thể có thuật toán O ( N ) . Ω(NloglogN)O(N)
Yuval Filmus

1
Từ đây, có vẻ như bắt (và yếu tố phi tuyến tính bổ sung) đến từ hai vòng lặp foreach () trong vòng lặp while chính của bạn; tổng thời gian của bạn về cơ bản là và vấn đề là kích thước của hiList và loList không nhất thiết bị ràng buộc bởi bất kỳ hằng số nào. i=0N/2|hiListNi||loListi|
Steven Stadnicki

Vâng, điều đó đúng, tuy nhiên công thức của bạn hơi lạc hậu vì lần đầu tiên tôi dao động từ 0 đến apprx. N PI / 8 và chỉ đứng thứ hai một phần các giá trị của i thỏa mãn hiList (Ni) + loList (i) = N, vì vậy chúng không được thêm vào. Trong mọi trường hợp, không có cách nào khắc phục điều này và tôi khá chắc chắn rằng điều này mang lại độ phức tạp tối thiểu có thể có của O (N log (log (N))).
RBarryYoung

Nhưng chúng ta có thể có một thuật toán chạy trong O (max (N, "số lượng giải pháp")), lấy không gian O (n).
gnasher729

Câu trả lời:


15

Thuật toán của Juho có thể được cải thiện thành thuật toán bằng cách sử dụng đáp ứng ở giữa. Đi qua tất cả các cặp A , B O(N) ; cho mỗi cặp như vậyM=A2+B2N, cửa hàng(A,B)trong một số mảngTcó độ dàiN(mỗi vị tríMcó thể chứa một số cặp, mà có thể được lưu trữ trong một danh sách liên kết). Bây giờ đi qua các cặpM,N-Msao cho các ô tương ứng trongTkhông trống.A,BNM=A2+B2N(A,B)TNMM,NMT

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

N


Hmm, điều gặp gỡ ở giữa nghe có vẻ rất giống với những gì tôi đang làm (gần như đã hoàn thành), đó là thuật toán Hợp nhất tăng dần / giảm dần so với các cặp TwoSapes. Điều đó có giống nhau không?
RBarryYoung

1
Có lẽ giống nhau, gặp gỡ ở giữa là một heuristic phổ biến đến mức nó phải có nhiều tên khác nhau.
Yuval Filmus

σ(N)

σ(N)ο(N)

1
Tổng các hàm chia thực sự.
Yuval Filmus

5

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

O(log2n)O(log2nloglogn)


[1] MO Rabin, JO Shallit, Thuật toán ngẫu nhiên trong Lý thuyết số , Truyền thông về Toán học thuần túy và ứng dụng 39 (1986), không. S1, trang S239 dây S256 .


Đối với một thuật toán tầm thường, bạn chỉ cần các vòng lặp cho A, B và C rồi tính D và kiểm tra xem nó có phải là số nguyên không. Nếu bạn yêu cầu A ≤ B ≤ C D, bạn sẽ nhận được O (N ^ 1.5) với hằng số khá nhỏ.
gnasher729

Khoảng 0,04 N ^ 1,5 bộ ba (A, B, C) và kiểm tra xem N - A ^ 2 - B ^ 2 - C ^ 2 là một hình vuông có thể được thực hiện rất nhanh.
gnasher729

-2

8ddn


1
Và làm thế nào để trả lời câu hỏi này? Nhiệm vụ là cung cấp cho tất cả các bộ tứ!
Raphael

1
Điều này đã được đề cập trong câu trả lời của tôi.
Yuval Filmus
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.