迭代两个正方形的总和

时间:2013-11-21 11:38:58

标签: algorithm

我正在寻找一种有效的方法来迭代由a^2+b^2定义的无限非递减序列,其中ab都是正整数。我在这里迭代的意思是,给定现有的n条目列表,算法应该有效(我希望O(log(m)),其中m=a^2+b^2)找到下一个条目。 / p>

此列表的开头是:1 ^ 2 + 1 ^ 2 = 2,1 ^ 2 + 2 ^ 2 = 5,2 ^ 2 + 2 ^ 2 = 8,1 ^ 2 + 3 ^ 2 = 10, 2 ^ 2 + 3 ^ 2 = 13,1 ^ 2 + 4 ^ 2 = 17,...

这是用于生成这些条目的python代码(前100个是正确的):

xs=[]
for i in range(1, 100):
    for j in range(i, 100):
        xs.append((i**2+j**2, i, j))
xs.sort()

我查看了列表的开头,看不到任何模式。有人知道这样做的算法吗?

[edit]经过一些搜索,我发现Cornacchia's algorithm需要计算二次余数。然而,我仍然希望有更好的东西,因为我们已经知道迭代过程中的先前数字。

2 个答案:

答案 0 :(得分:2)

isSumOfSquares函数返回True如果 n 可以写为大于零的平方和,False则基于Edsgar的算法Dijkstra的1976年着作编程学科 x n 的平方根向下扫描,当正方形的总和太大时,它会减少,并且 y 从1向上扫过,当正方形的总和太小时会增加。

from math import sqrt

def isSumOfSquares(n):
    x, y = int(sqrt(n)), 1
    while x >= y:
        if x*x + y*y < n: y = y + 1
        elif x*x + y*y > n: x = x - 1
        else: return True
    return False

filter(isSumOfSquares, range(1,100))

这将返回列表[2,5,8,10,13,17,18,20,25,26,29,32,34,37,40,41,45,50,52,53,58, 61,65,68,72,73,74,80,82,85,89,90,97,98]。我在my blog讨论了一个类似的问题。

答案 1 :(得分:0)

在考虑这个问题时,我假设了两件事。第一个是对于任何a ^ 2 + b ^ 2 a> b。这只是因为平凡(a,b)与(b,a)在排序中的位置相同,因此您不需要同时考虑这两种情况。

如果允许a或b为0,则解释也会更容易。这些情况可以很容易地在真实算法中取出。

我的想法是你应该能够根据m=a+b做一些事情,并说对于给定的m,最小的平方和是m^2/2(当它们相等时)和最大的是(m-1)^2,两者之间有明显的顺序。然后,您可以在这些之间进行某种合并排序以查找下一个。

我的想法是,如果您查看m=a+b,那么对于任何给定的m,您可以为所有a+b=m创建一个有序的平方和列表。原因是我们知道,当ab为0时,总和是最大值,a=b=m/2时最小值。这意味着我们可以简单地生成一系列有序的平方和L(m)的列表。

我们也知道L(m)中的最大和最小条目,因此在我们处于该范围之前,我们不需要开始考虑这些列表。

它仍然意味着可能需要考虑较高值的大量列表,但它仍然比更强力的方法更好。

我不确定是否有更好的方法,一旦你有这些列表,但要弄清楚你需要选择哪一个。我想可能不是你可以根据列表中数字的分布做出一些猜测,但我想不出一种方法可以很好地形式化它们。

为了给出一个更直观的例子

List      Lowest value    Highest value
L(0)            0  (0,0)        0  (0,0)
L(1)            1  (1,0)        1  (1,0)
L(2)            2  (1,1)        4  (2,0)
L(3)            5  (2,1)        9  (3,0)
L(4)            8  (2,2)        16 (4,0)
L(5)            13 (3,2)        25 (5,0)
L(6)            18 (3,3)        36 (6,0)
L(7)            25 (4,3)        49 (7,0)
L(8)            32 (4,4)        64 (8,0)

所以从0开始你只需要考虑L(0)中的项目,那就是用尽了你移动到L(1)。然后就已经筋疲力尽了等等。当你通过L(3)工作时,你发现你正在看L(3)的最后一个是9,它大于L(4)中的最低点,所以你看看这两个列表。

当您刚刚输入(5,0)和(4,3)25时,您将考虑L(6)和L(7)中的事物。当你走得更高时,你会考虑更多的列表,因为列表开始在更大的范围内包含更多的项目(例如,数字10,000在L(100)(几乎)和L(141)(从9941开始)的范围内)。

我还应该注意,你不需要提前计算整个列表,你可以轻松地使它成为一个懒惰的迭代器,只计算列表前面的数字,因为这是你唯一想要的。