我试图解决this MaxCollatzLength kata,但我正在努力优化它,以便为真正的大数字运行得足够快。
在这个kata中,我们将看看collatz序列的长度。 以及它们如何发展。写一个取整数n的函数 并返回1到n之间具有最大Collatz的数字 序列长度和最大长度。输出必须采取 数组的形式[number,maxLength]例如,Collatz序列 4是[4,2,1],3是[3,10,5,16,8,4,2,1],2是[2,1],1是[1],所以 MaxCollatzLength(4)应该返回[3,8]。如果n不是正数 整数,函数必须返回[]。
如您所见,Collatz序列中的数字可能超过n。最后 测试使用随机大数字,因此您可以考虑进行一些优化 你的代码:
你可能会非常不走运,只能得到硬数据:尝试提交2-3 如果它超时的时间;如果它仍然存在,可能你需要优化 你的代码更多;
优化1:计算a的长度时 顺序,如果n是奇数,那么3n + 1是多少?
优化2:当循环1到n时,取i使得i <不,2,什么 将是2i的序列长度?
递归解决方案可以快速打击堆栈,因此我使用了while循环。我想我已经理解并应用了第一个优化。我还发现,对于n是2的幂,最大长度将是(log2 of n)+ 1(这对于一个非常大的数字只会刮掉很短的时间)。最后,我已经记住了到目前为止计算的折叠长度,以避免重新计算。
然而,我并不了解第二次优化的含义。我试图注意到一个带有一些随机样本和循环的模式,并且我已经绘制了n&lt; n&lt; 50000.我注意到它似乎大致跟随曲线,但我不知道如何继续 - 这是一个红鲱鱼吗?
我理想地寻找正确方向的提示,以便我自己努力解决方案。
function collatz(n) {
let result = [];
while (n !== 1) {
result.push(n);
if (n % 2 === 0) n /= 2;
else {
n = n * 3 + 1;
result.push(n);
n = n / 2;
}
}
result.push(1);
return result;
}
function collatzLength(n) {
if (n <= 1) return 1;
if (!collatzLength.precomputed.hasOwnProperty(n)) {
// powers of 2 are logarithm2 + 1 long
if ((n & (n - 1)) === 0) {
collatzLength.precomputed[n] = Math.log2(n) + 1;
} else {
collatzLength.precomputed[n] = collatz(n).length;
}
}
return collatzLength.precomputed[n];
}
collatzLength.precomputed = {};
function MaxCollatzLength(n) {
if (typeof n !== 'number' || n === 0) return [];
let maxLen = 0;
let numeralWithMaxLen = Infinity;
while (n !== 0) {
let lengthOfN = collatzLength(n);
if (lengthOfN > maxLen) {
maxLen = lengthOfN;
numeralWithMaxLen = n;
}
n--;
}
return [numeralWithMaxLen, maxLen];
}
答案 0 :(得分:3)
记忆是这里表现良好的关键。您会记住计算Collatz序列的函数的最终结果。这将帮助您重复调用maxCollatzLength
,但不会在第一次确定序列长度时帮助您。
另外,正如@j_random_hacker所提到的,没有必要将序列实际创建为列表;它足以存储它的长度。整数结果重量轻,可以轻松记忆。
当您确定Collatz序列的长度时,您可以使用预先计算的结果。不要一直按顺序执行,而是按照它直到找到已知长度的数字。
您进行的其他优化是微优化。我不确定计算两个权力的日志真的可以买到任何东西。它会给你额外的考验带来负担。
下面的memoized实现甚至放弃了检查1,最初将1放在预先计算值的字典中。
var precomp = {1: 1};
function collatz(n) {
var orig = n;
var len = 0;
while (!(n in precomp)) {
n = (n % 2) ? 3*n + 1 : n / 2;
len++;
}
return (precomp[orig] = len + precomp[n]);
}
function maxCollatz(n) {
var res = [1, 1];
for (var k = 2; k <= n; k++) {
var c = collatz(k);
if (c > res[1]) {
res[0] = k;
res[1] = c;
}
}
return res;
}
我还没有使用过node.js,而是使用了我的Firefox中的JavaScript。它提供了合理的性能。我首先使用collatz
作为递归函数,这使得实现只比你的实现稍快。
问题中提到的第二个优化意味着,如果您知道C(n)
,那么您也知道C(2*n) == C(n) + 1
。您可以使用该知识以自下而上的方式预先计算所有偶数n
的值。
如果Collatz序列的长度可以从下往上计算,有点像Erathostenes的筛子,那将是很好的。你必须知道你来自哪里而不是去哪里,但是很难知道ehen要停下来,因为为了找到n < N
的最长序列,你必须计算出许多超出界限的序列{ {1}}。因此,备忘录是一种避免重复的好方法。