我编写了一个Y版本,它使用memoization自动将旧值缓存在闭包中。
var Y = function (f, cache) {
cache = cache || {};
return function (x) {
if (x in cache) return cache[x];
var result = f(function (n) {
return Y(f, cache)(n);
})(x);
return cache[x] = result;
};
};
现在,当almostFibonacci
(定义如下)传递给上面的函数时,它会很容易地返回一个大的Fibonacci数的值。
var almostFibonacci = function (f) {
return function (n) {
return n === '0' || n === '1' ? n : f(n - 1) + f(n - 2);
};
};
然而,在某个值(Number.MAX_SAFE_INTEGER
)之后,JavaScript中的整数(由于它们的 IEEE-754双精度格式)不准确。因此,考虑到上面Fibonacci函数中唯一的数学运算是加法和减法这一事实,并且因为运算符不能在JavaScript中重载,我编写了和函数和差函数的天真实现(两者都使用字符串支持大整数)如下。
String.prototype.reverse = function () {
return this.split('').reverse().join('');
};
var difference = function (first, second) {
first = first.reverse();
second = second.reverse();
var firstDigit,
secondDigit,
differenceDigits = [],
differenceDigit,
carry = 0,
index = 0;
while (index < first.length || index < second.length || carry !== 0) {
firstDigit = index < first.length ? parseInt(first[index], 10) : 0;
secondDigit = index < second.length ? parseInt(second[index], 10) : 0;
differenceDigit = firstDigit - secondDigit - carry;
differenceDigits.push((differenceDigit + (differenceDigit < 0 ? 10 : 0)).toString());
carry = differenceDigit < 0 ? 1 : 0;
index++;
}
differenceDigits.reverse();
while (differenceDigits[0] === '0') differenceDigits.shift();
return differenceDigits.join('');
};
var sum = function (first, second) {
first = first.reverse();
second = second.reverse();
var firstDigit,
secondDigit,
sumDigits = [],
sumDigit,
carry = 0,
index = 0;
while (index < first.length || index < second.length || carry !== 0) {
firstDigit = index < first.length ? parseInt(first[index], 10) : 0;
secondDigit = index < second.length ? parseInt(second[index], 10) : 0;
sumDigit = firstDigit + secondDigit + carry;
sumDigits.push((sumDigit % 10).toString());
carry = sumDigit > 9 ? 1 : 0;
index++;
}
sumDigits.reverse();
while (sumDigits[0] === '0') sumDigits.shift();
return sumDigits.join('');
};
现在,这些功能本身就完美无缺。 1
我现在更新了almostFibonacci
函数,如下所示,使用 sum 函数代替 + 和差异函数而不是 - 运算符。
var almostFibonacci = function (f) {
return function (n) {
return n === '0' || n === '1' ? n : sum(f(difference(n, '1')), f(difference(n, '2')));
};
};
正如您可能已经猜到的那样,这确实有效。如果像10这样的小数字,它会崩溃小提琴。
问题:可能出现什么问题?这里的所有功能都完美无瑕。但同时,他们似乎失败了。这里的任何人都可以帮我调试这个特别复杂的场景吗?
1 除了差函数的边缘情况。它要求第一个参数大于第二个参数。
答案 0 :(得分:2)
现在,它们本身都可以完美地工作 - 除了差异函数的边缘情况。它要求第一个参数大于第二个参数。
这就是问题所在。在您的斐波纳契算法中,您在某个时刻计算difference("2", "2")
,这需要让"0"
生效。但它会返回空字符串""
,它不会作为递归的保护条件进行测试。在计算difference("", "1")
的下一步中,该函数将陷入无限循环。
解决方案:
不要使用字符串作为序数,但仅限于斐波纳契数本身。你几乎不会尝试计算(2 53 +1)的斐波那契数,你呢?我认为这也是一个显着的速度提升。
var fibonacci = Y(function(fib) {
return function(n) {
if (n == 0) return "0";
if (n == 1) return "1";
return sum(fib(n-1), fib(n-2));
};
});
答案 1 :(得分:-1)
以下是我解决手头问题的方法。
<强>更改强>:
while (differenceDigits[0] === '0') differenceDigits.shift();
声明。即使输出差异而没有截断的前导零,如果出现像'0'
这样的边缘情况,它会输出difference('2', '2')
。almostFibonacci
函数中的return语句编辑为return n == 0 || n == 1 ? n : sum(f(difference(n, '1')), f(difference(n, '2')));
。请注意,我使用非严格相等运算符检查0而不是'0'。 1 1 我正在n == 0
而不是n === '0'
的原因是因为在JavaScript中,'00000' == 0
只有'00000' !== '0'
和在我新更新的差异函数中,没有截断的前导零,我无法保证零输出的零数。嗯,实际上我可以。 n的长度将为零。