我们能快找到所有总和为N的四平方组合吗?


12

在Stack Overflow(此处)提出了一个问题:

给定一个整数,打印出的整数值的所有可能的组合和其中求解方程式。A B C D A 2 + B 2 + C 2 + D 2 = NNA,B,CDA2+B2+C2+D2=N

这个问题当然与Bachet的数论猜想有关(由于他的证明,有时也称为Lagrange的四平方定理)。有一些论文讨论了如何找到一个解决方案,但是我一直找不到任何关于我们能够以多快的速度找到特定所有解决方案的信息(即所有组合,而不是全部排列)。N

我已经考虑了很多,在我看来,它可以在时空中求解,其中是所需的总和。但是,由于缺乏有关该主题的任何先验信息,我不确定这是否对我而言是重要的主张,还是仅是微不足道的,显而易见的或已知的结果。NO(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);
    }

还有一个函数返回所有从0到N求和的TwoSquare对:

    // 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),但是正如Yuval Filmus所指出的那样,由于N的四平方解的数量可以是大约(N ln ln N),所以该算法不可能是少于那个。


是的,请张贴。我仍在开发线性算法的详细信息,但我很确定它是有效的。
RBarryYoung'8

5
作为记录,似乎有时有多达解决方案,所以我们实际上不能拥有算法。O N Ω(NloglogN)O(N)
Yuval Filmus,2012年

1
从这里看来,捕获(以及额外的非线性因子)来自主while循环中的两个foreach()循环;您的总时间基本上是,问题是hiList和loList的大小不一定受任何常数限制。i=0N/2|hiListNi||loListi|
史蒂文·斯塔德尼基

是的,这是正确的,但是您的公式有些偏离,因为首先我的范围是0到apprx。N PI / 8,仅第二个i值的一小部分满足hiList(Ni)+ loList(i)= N,因此并没有全部添加。无论如何,没有办法解决这个问题,我很漂亮确保这使O(N log(log(N)))的复杂度最小
RBarryYoung'8

但是我们可以拥有一种算法,该算法可以在O(最大值(N,“解数”)个)中运行,占用O(n)空间。
gnasher729

Answers:


15

使用中间相遇可以将Juho算法改进为算法。遍历所有对 ; 对于每对,将存储在长度为某个数组中(每个位置可能包含几对,可能存储在链接列表中)。现在遍历对,以使中的对应单元格为非空。O(N)中号=2+2ÑŤÑ中号中号ñ-中号ŤA,BNM=A2+B2N(A,B)TNMM,NMT

这样,我们得到所有四倍体的隐式表示。如果我们要列出所有这些,那么我们就比更好,因为Jacobi的四平方定理表明(对于奇数)表示的个数是,并且有无限多个整数,例如(请参阅格伦沃尔定理)。ñ 8 σ Ñ σ Ñ Ë γ - ε ñ 登录登录ÑΩ(NloglogN)N8σ(N)σ(N)(eγϵ)NloglogN

为了获得较少的平凡算法,可以尝试在适当的四元数环上分解,因为我们知道,通过拉格朗日的四平方恒等式,在一定意义上,作为两个平方和的表示与这些分解有关。我们仍然需要找到任何相关素数的所有表示形式。N


嗯,在中间相遇的声音听起来与我正在(几乎完成)的工作非常相似,这是在TwoSquare对上的升/降Merge-Match算法。听起来一样吗?
RBarryYoung

1
可能是相同的,在中间相遇是一种常见的启发式方法,它必须具有许多不同的名称。
Yuval Filmus 2012年

嗯,我已经离开学术界三十年了,是什么意思?(或者您能给我指一下吗?)。σ(N)
RBarryYoung

还是确实是?ο N σ(N)ο(N)
RBarryYoung

1
除数之和的确起作用。
Yuval Filmus 2012年

5

我认为时间算法不是一个微不足道的算法,如果存在,则需要一些洞察力。二次时间运行的明显算法会枚举所有元组。这可以在四个循环中完成,因此总时间复杂度变为。它还清楚地列举了所有解决方案。Ç d o(N2) ON2A,B,C,DNO(N2)

作为相关算法,Rabin和Shallit [1]提出了两种用于将整数分解为平方和的随机算法。对于两个平方,它们给出了期望时间算法。对于四个平方,它们给出了预期时间算法。请注意,这些算法并不能为您提供所有解决方案,而仅仅是一种解决方案。O log 2 n log log n O(log2n)O(log2nloglogn)


[1] MO Rabin,JO Shallit,《数论中的随机算法》,《纯数学和应用数学通讯》(1986年),第39期。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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.