将自然数表示为不同方块的总和

时间:2014-10-06 18:43:16

标签: algorithm number-theory

问题是找到正整数的最大集合S,使得S的元素的平方和等于给定的数字n。

例如:

  

4 =2²
  20 =4²+2²
  38 =5²+3²+2²
  300 =11²+8²+7²+6²+4²+3²+2²+1²。

我有一个在时间O(2^(sqrt n) * n)内运行的算法,但它太慢了(每个方块的子集)。

3 个答案:

答案 0 :(得分:7)

基于subset sum的规范动态程序,有一个O(n^1.5) - 时间算法。这是重复:

C(m, k) is the size of the largest subset of 1..k whose squares sum to m
C(m, k), m < 0 = -infinity (infeasible)
C(0, k) = 0
C(m, 0), m > 0 = -infinity (infeasible)
C(m, k), m > 0, k > 0 = max(C(m, k-1), C(m - k^2, k-1) + 1)

C(m, k)中的所有m0..n中的所有k计算0..floor(n^0.5)。返回C(n, floor(n^0.5))作为目标值。要恢复该集,请追溯argmaxes。

答案 1 :(得分:4)

我只是想知道这个问题是否会减少到NP?看起来你有小于n的整数(正方形)列表(可以在O(sqrt(n))中生成),并且你正在寻找1 to sqrt(n)大小的子集和(检查所有可能性) )。如果是这样,它应该可以解决背包动态编程算法(但这是非常天真的算法,我认为可以改进)在O(n^2) - sqrt(n)的问题中检查时间sqrt(n)背包项目计数次数n背包重量。

编辑: 我认为在填充动态编程数组后,你可以在O(n*sqrt(n))中完成智能回溯。

答案 2 :(得分:4)

您可以使用重复:

T(0, m) = 0
T(n, m) = -Infinity (if n<0 or m<0)
T(n, m) = max(T(n-m*m, m-1)+1, T(n, m-1))

或者,在Python代码中:

from functools import lru_cache

@lru_cache(100000)
def T(n, m):
    if n<0 or m<0: return (-1000000, 0)
    if n==0: return (0, 0)
    return max((T(n-m*m, m-1)[0]+1, m), T(n, m-1)) 

def squares(n):
    s = int(n**0.5)
    while n>0 and s>0:
        _, factor = T(n, s)
        yield factor**2
        n -= factor**2
        s = factor-1

for x in (4, 20, 38, 300):
    result = list(squares(x))
    print(sum(result), '= sum', result) 

您给出的示例(300)可以用8个因子编写:

300 =11²+8²+7²+6²+4²+3²+2²+1²

其他结果:

4 = sum [4]
20 = sum [16, 4]
38 = sum [25, 9, 4]
300 = sum [121, 64, 49, 36, 16, 9, 4, 1]