使用动态编程将自然数表示为平方和

时间:2010-10-19 11:19:55

标签: algorithm

问题是找到求和数n所需的最小平方数。

一些例子:

min[ 1] = 1 (1²)
min[ 2] = 2 (1² + 1²)
min[ 4] = 1 (2²)
min[13] = 2 (3² + 2²)

我知道Lagrange's four-square theorem表示任何自然数都可以表示为四个方格的总和。

我正在尝试使用DP来解决这个问题。

这就是我提出的(不正确)

min[i] = 1 where i is a square number
min[i] = min(min[i - 1] + 1, 1 + min[i - prev]) where prev is a square number < i

解决此问题的DP方法是什么?

4 个答案:

答案 0 :(得分:14)

我不确定DP是否是解决此问题的最有效方法,但您要求使用DP。

min [i] = min(min [i-1] + 1,1 + min [i-prev])其中prev是平方数&lt;我
这很接近,我会写条件为

min[i] = min(1 + min[i - prev]) for each square number 'prev <= i'

注意,对于每个i,您需要检查prev的不同可能值。

这是Java中的简单实现。

Arrays.fill(min, Integer.MAX_VALUE);
min[0] = 0;
for (int i = 1; i <= n; ++i) {
    for (int j = 1; j*j <= i; ++j) {
        min[i] = Math.min(min[i], min[i - j*j] + 1);
    }
}

答案 1 :(得分:5)

我觉得你很亲密......

你正在接受两个术语的min(),每个术语都是min[i - p] + 1,其中p是1或其他一些正方形&lt;岛

要解决此问题,只需将min[i - p] + 1的min()超过所有 p(其中p是平方&lt; i)。

那将是一个正确的方式。可能有更快的方式。

此外,如果您提供min[]min()个不同的名称,它可能有助于提高可读性。 : - )

P.S。上述方法要求您明确地或作为DP框架的一部分记住min[]。否则,由于递归,算法的复杂性将类似于O(sqrt(n)!): - p尽管平均情况可能要好得多。

P.P.S。请参阅@ Nikita的答案以获得良好的实施方案。我将添加以下优化...(我不是在挑剔他的实现 - 他把它作为一个简单的实现。)

  1. 在进入外环之前检查n是否是完美的正方形:如果是,则min [n] = 1并且我们已完成。
  2. 在进入内循环之前检查我是否是一个完美的正方形:如果是,min [i] = 1,并跳过内循环。
  3. 如果min [i]设置为2,则打破内循环,因为它不会变得更好(如果可以用一个方格完成,我们将永远不会进入内循环,这要归功于之前的优化)。
  4. 我想知道是否可以改变内环上的终止条件以减少迭代次数,例如j*j*2 <= i甚至是j*j*4 <= i。我是这么认为的,但我还没有完全了解它。
  5. 对于大i,在内循环之前计算j的限制会更快,并且在循环终止条件下直接比较j,而不是在每个内循环迭代中对j进行平方。 E.g。

    float sqrti = Math.sqrt(i);
    for (int j = 1; j <= sqrti; ++j) {
    

    另一方面,无论如何你需要j ^ 2进行递归步骤,所以只要你存储它,你也可以使用它。

答案 2 :(得分:0)

对于多样性,这是另一个答案:

将minsq [i,j]定义为总和为i的{1 ^ 2,2 ^ 2,...,j ^ 2}的最小平方数。然后递归是:

minsq[i, j] = min(minsq[i - j*j, j] + 1, minsq[i, j - 1])

即,要计算minsq [i,j],我们要么使用j ^ 2,要么我们不使用。我们对n的回答是:

minsq[n, floor(sqrt(n))]

这个答案可能在概念上比前面提到的更简单,但在代码方面它更加困难,因为需要小心基本情况。两个答案的时间复杂度渐近相同。

答案 3 :(得分:0)

我提出了一种通用的非常有效的动态编程算法,用于找到给定功率的最小正整数,以便在JavaScript中达到给定目标。

例如,要获得具有4次幂的整数的50000,结果将为[10,10,10,10,10]或达到18571,并且将产生具有7次幂的整数[3,4]。该算法甚至可以使用合理的幂,例如用 3 / 5 的整数达到222 [ 32, 32, 243, 243, 243, 3125 ] < / p>

&#13;
&#13;
function getMinimumCubes(tgt,p){
  var maxi = Math.floor(Math.fround(Math.pow(tgt,1/p))),
      hash = {0:[]},
       pow = 0,
         t = 0;
  for (var i = 1; i <= maxi; i++){
    pow = Math.fround(Math.pow(i,p));
    for (var j = 0; j <= tgt - pow; j++){
      t = j + pow;
      hash[t] = hash[t] ? hash[t].length <= hash[j].length ? hash[t]
                                                           : hash[j].concat(i)
                        : hash[j].concat(i);
    }
  }
  return hash[tgt];
}

var target = 729,
    result = [];
console.time("Done in");
result = getMinimumCubes(target,2);
console.timeEnd("Done in");
console.log("Minimum number of integers to square and add to reach", target, "is", result.length, "as", JSON.stringify(result));

console.time("Done in");
result = getMinimumCubes(target,6);
console.timeEnd("Done in");
console.log("Minimum number of integers to take 6th power and add to reach", target, "is", result.length, "as", JSON.stringify(result));

target = 500;
console.time("Done in");
result = getMinimumCubes(target,3);
console.timeEnd("Done in");
console.log("Minimum number of integers to cube and add to reach", target, "is", result.length, "as", JSON.stringify(result));

target = 2017;
console.time("Done in");
result = getMinimumCubes(target,4);
console.timeEnd("Done in");
console.log("Minimum number of integers to take 4th power and add to reach", target, "is", result.length, "as", JSON.stringify(result));

target = 99;
console.time("Done in");
result = getMinimumCubes(target,2/3);
console.timeEnd("Done in");
console.log("Minimum number of integers to take 2/3th power and add to reach", target, "are", result);
&#13;
&#13;
&#13;