我认为应该指出的一件事是,还有其他方法可以实现 fib,这对于 C++ 之类的东西来说更容易计算
考虑下面的伪代码
function fib (n) {
let a = 0, b = 1, _;
while (n > 0) {
_ = a;
a = b;
b = b + _;
n = n - 1;
}
return a;
}
这不需要记忆,您不必担心过多的递归调用会炸毁您的堆栈。递归是一个非常强大的循环结构,但它是最好留给像 Lisp、Scheme、Kotlin、Lua(和其他一些语言)这样优雅地支持它的语言。
这并不是说在 C++ 中消除尾调用是不可能的,但除非您明确地对其进行优化/编译,否则我怀疑您使用的任何编译器是否会默认支持它。
至于计算异常大的数字,您必须要么创造性地添加 The Hard Way,要么依赖于任意精度的算术库,如 GMP。我敢肯定还有其他的库。
添加 Hard Way™
还记得你小时候是如何添加大数字的吗?刚从铝箔纸上取下来?
5 岁数学
1259601512351095520986368
+ 50695640938240596831104
---------------------------
?
你必须从右到左添加每一列。当一列溢出到两位数时,记得把那个 1 带到下一列。
... <-001
1259601512351095520986368
+ 50695640938240596831104
---------------------------
... <-472
第 10,000 个斐波那契数有数千位数长,因此无法适应 C++ 开箱即用的任何整数。因此,无需依赖库,您就可以使用字符串或一位数的数组。要输出最终数字,您必须将其转换为字符串。
(woflram alpha: fibonacci 10000)
这样做,您将执行几百万个个位数的加法;这可能需要一段时间,但对于任何现代计算机来说都应该是轻而易举的事情。是时候开始工作了!
这是 JavaScript 中 Bignum 模块的示例
const Bignum =
{ fromInt: (n = 0) =>
n < 10
? [ n ]
: [ n % 10, ...Bignum.fromInt (n / 10 >> 0) ]
, fromString: (s = "0") =>
Array.from (s, Number) .reverse ()
, toString: (b) =>
b .reverse () .join ("")
, add: (b1, b2) =>
{
const len = Math.max (b1.length, b2.length)
let answer = []
let carry = 0
for (let i = 0; i < len; i = i + 1) {
const x = b1[i] || 0
const y = b2[i] || 0
const sum = x + y + carry
answer.push (sum % 10)
carry = sum / 10 >> 0
}
if (carry > 0) answer.push (carry)
return answer
}
}
我们可以验证上面的 Wolfram Alpha 答案是否正确
const { fromInt, toString, add } =
Bignum
const bigfib = (n = 0) =>
{
let a = fromInt (0)
let b = fromInt (1)
let _
while (n > 0) {
_ = a
a = b
b = add (b, _)
n = n - 1
}
return toString (a)
}
bigfib (10000)
// "336447 ... 366875"
展开下面的程序在浏览器中运行
const Bignum =
{ fromInt: (n = 0) =>
n < 10
? [ n ]
: [ n % 10, ...Bignum.fromInt (n / 10 >> 0) ]
, fromString: (s = "0") =>
Array.from (s) .reverse ()
, toString: (b) =>
b .reverse () .join ("")
, add: (b1, b2) =>
{
const len = Math.max (b1.length, b2.length)
let answer = []
let carry = 0
for (let i = 0; i < len; i = i + 1) {
const x = b1[i] || 0
const y = b2[i] || 0
const sum = x + y + carry
answer.push (sum % 10)
carry = sum / 10 >> 0
}
if (carry > 0) answer.push (carry)
return answer
}
}
const { fromInt, toString, add } =
Bignum
const bigfib = (n = 0) =>
{
let a = fromInt (0)
let b = fromInt (1)
let _
while (n > 0) {
_ = a
a = b
b = add (b, _)
n = n - 1
}
return toString (a)
}
console.log (bigfib (10000))