我试图解决此代码战kata,Square into Squares。
我通过了大部分测试,但有两个输入,我的算法超出了最大调用堆栈大小。
我觉得我在照顾所有的边缘条件,而且我无法弄清楚我错过了什么。
function sumSquares (n) {
function decompose (num, whatsLeft, result) {
if (whatsLeft === 0) return result
else {
if (whatsLeft < 0 || num === 0) return null
else {
return decompose(num-1, whatsLeft - num * num, [num].concat(result)) || decompose(num-1, whatsLeft, result)
}
}
}
return decompose(n-1, n*n, [])
}
const resA = sumSquares(50) //[1,3,5,8,49]
console.log(resA)
const resB = sumSquares(7) //[2,3,6]
console.log(resB)
const resC = sumSquares(11) //[ 1, 2, 4, 10 ]
console.log(resC)
const res1 = sumSquares(90273)
console.log(res1)
const res2 = sumSquares(123456)
console.log(res2)
&#13;
答案 0 :(得分:1)
看起来您的代码是正确的,但有两个问题:首先,您的调用堆栈最终会达到大小&#34; num&#34; (这可能会导致大输入失败),其次,它可能会多次重新计算相同的值。
第一个问题很容易解决:您可以跳过导致num
结果为whatsLeft
的{{1}}值。像这样:
while(num * num > whatsLeft) num = num - 1;
您可以在第一个if
语句后插入此内容。这也使您可以删除对否定whatsLeft
的检查。作为一种风格问题,我在返回后删除了if语句的else{}
个案例 - 这减少了缩进,并且(我认为)使代码更容易阅读。但这仅仅是个人品味的问题。
function sumSquares (n) {
function decompose (num, whatsLeft, result) {
if (whatsLeft === 0) return result;
while (num * num > whatsLeft) num -= 1;
if (num === 0) return null;
return decompose(num-1, whatsLeft - num * num, [num].concat(result)) || decompose(num-1, whatsLeft, result);
}
return decompose(n-1, n*n, []);
}
您的测试用例会立即针对我进行这些更改,因此第二个问题(可通过记忆解决)无需解决。我也试过在codewars网站上提交它,稍微调整一下(外部函数需要调用decompose
,所以外部函数和内部函数都需要重命名),所有113个测试用例都传递859ms。
答案 1 :(得分:1)
@PaulHankin的回答提供了很好的见解
让我们看sumSquares (n)
n = 100000
decompose (1e5 - 1, 1e5 * 1e5, ...)
在第一帧中,
num = 99999
whatsLeft = 10000000000
哪个产生
decompose (99999 - 1, 1e10 - 99999 * 99999, ...)
第二帧是
num = 99998
whatsLeft = 199999
问题在于:num * num
上面的whatsLeft
显着大于num
,每次我们重复尝试新的-1
时,我们每帧只减少decompose (99998 - 1, 199999 - 99998 * 99998, ...)
。没有修复任何东西,产生的下一个过程将是
num = 99997
whatsLeft = -9999500005
第三帧是
whatsLeft
了解num
显着消极的方式?这意味着在下一个值不会导致whatsLeft
降至零以下之前,我们必须经常减少// [frame #4]
num = 99996
whatsLeft = -9999000017
// [frame #5]
num = 99995
whatsLeft = -9998800026
...
// [frame #99552]
num = 448
whatsLeft = -705
// [frame #99553]
num = 447
whatsLeft = 190
sumSquares (100000)
正如我们上面所看到的,只需要估计decompose
的第二位数就需要近100000帧。这正是Paul Hankin描述的第一个问题。
如果我们只使用num
查看num
,我们也可以更容易想象它。下面,如果找不到解决方案,堆栈将增长到num
大小,因此不能用于计算// imagine num = 100000
function decompose (num, ...) {
...
decompose (num - 1 ...) || decompose (num - 1, ...)
}
超出堆栈限制的解决方案
while
Paul的解决方案使用num
循环使用循环递减num
,直到guess
足够小。另一种解决方案是通过找到剩余的whatsLeft
const sq = num * num
const next = whatsLeft - sq
const guess = Math.floor (Math.sqrt (next))
return decompose (guess, next, ...) || decompose (num - 1, whatsLeft, ...)
num
现在它可用于计算console.log (sumSquares(123456))
// [ 1, 2, 7, 29, 496, 123455 ]
巨大的值
console.log (sumSquares(50))
// [ 1, 1, 4, 9, 49 ]
但请注意,某些输入存在错误。解决方案的平方仍然总和到正确的数量,但它允许重复一些数字
Math.min
要强制执行严格增加的要求,我们必须确保计算的猜测仍然低于之前的值。我们可以使用const guess = Math.floor (Math.sqrt (next))
const guess = Math.min (num - 1, Math.floor (Math.sqrt (next)))
console.log (sumSquares(50))
// [ 1, 1, 4, 9, 49 ]
// [ 1, 3, 5, 8, 49 ]
现在修复了错误
function sumSquares (n) {
function decompose (num, whatsLeft, result) {
if (whatsLeft === 0)
return result;
if (whatsLeft < 0 || num === 0)
return null;
const sq = num * num
const next = whatsLeft - sq
const guess = Math.min (num - 1, Math.floor (Math.sqrt (next)))
return decompose(guess, next, [num].concat(result)) || decompose(num-1, whatsLeft, result);
}
return decompose(n-1, n*n, []);
}
console.log (sumSquares(50))
// [ 1, 3, 5, 8, 49 ]
console.log (sumSquares(123456))
// [ 1, 2, 7, 29, 496, 123455 ]
完整的程序演示
Array(1, 2, 3).asInstanceOf[Array[Any]]
&#13;