我发送了这个非常好的非递归函数来计算斐波那契序列。
所以我编写了一些c#并且能够验证所有高达1474的数字是否正确。
尝试计算1475及以上时出现问题。我的c#数学技能不能达到找出不同方法的任务。那么,有人有更好的方法在c#中表达这个特定的数学函数吗?除了传统的递归函数方式之外?
顺便说一下,我开始使用BigInteger作为返回类型。但是当试图将(1 + Math.Sqrt(5)/ 2)提升到1475次幂时,问题确实存在。我只是没有看到我需要什么样的数据类型(也没有这个问题的机制)来让它回到Infinity以外的东西。
这是一个起点。
private Double FibSequence(Int32 input) {
Double part1 = (1 / Math.Sqrt(5));
Double part2 = Math.Pow(((1 + Math.Sqrt(5)) / 2), input);
Double part3 = Math.Pow(((1 - Math.Sqrt(5)) / 2), input);
return (part1 * part2) - (part1 * part3);
}
而且,不,这不是家庭作业。对于缓慢的一天来说只是一个“简单”的问题。
答案 0 :(得分:14)
我不认为C#的数据类型具有足够的浮动精度和范围来处理这个问题。
如果你真的想沿着这条路走,你可以注意到共轭小于1,所以与舍入到最接近的整数相同,因此你可以简化你的找到的解决方案。然后使用二项式扩展,这样您只需要使用适当的 a 和 b 计算(这是合理的,可以使用BigInteger精确计算)。如果你仍然回到Double这个,你仍然不会比1475更远,但你应该能够弄清楚如何做这个部分只有精确的整数数学☺
使用矩阵求幂计算Fibonacci数的另一种简洁方法是:
如果你聪明的话,这可以在O(log n)中完成。
我最终在Haskell中实现了这些。 fib1
是矩阵求幂,fib2
是闭式公式的精确整数转换,如上所述。它们各自的运行时看起来像这样,由Criterion编译时由GHC 7.0.3衡量:
import Control.Arrow
import Data.List
import Data.Ratio
newtype Matrix2 a = Matrix2 (a, a, a, a) deriving (Show, Eq)
instance (Num a) => Num (Matrix2 a) where
Matrix2 (a, b, c, d) * Matrix2 (e, f, g, h) =
Matrix2 (a*e+b*g, a*f+b*h, c*e+d*g, c*f+d*h)
fromInteger x = let y = fromInteger x in Matrix2 (y, 0, 0, y)
fib1 n = let Matrix2 (_, x, _, _) = Matrix2 (1, 1, 1, 0) ^ n in x
binom n =
scanl (\a (b, c)-> a*b `div` c) 1 $
takeWhile ((/=) 0 . fst) $ iterate (pred *** succ) (n, 1)
evens (x:_:xs) = x : evens xs
evens xs = xs
odds (_:x:xs) = x : odds xs
odds _ = []
iterate' f x = x : (iterate' f $! f x)
powers b = iterate' (b *) 1
esqrt e n = x where
(_, x):_ = dropWhile ((<=) e . abs . uncurry (-)) $ zip trials (tail trials)
trials = iterate (\x -> (x + n / x) / 2) n
fib' n = (a, b) where
d = 2 ^ n
a = sum (zipWith (*) (odds $ binom n) (powers 5)) % d
b = sum (zipWith (*) (evens $ binom n) (powers 5)) % d
fib2 n = numerator r `div` denominator r where
(a, b) = fib' n
l = lcm (denominator a) (denominator a)
r = a + esqrt (1 % max 3 l) (b * b / 5) + 1 % 2
答案 1 :(得分:4)
using System;
using Nat = System.Numerics.BigInteger; // needs a reference to System.Numerics
class Program
{
static void Main()
{
Console.WriteLine(Fibonacci(1000));
}
static Nat Fibonacci(Nat n)
{
if (n == 0) return 0;
Nat _, fibonacci = MatrixPower(1, 1, 1, 0, Nat.Abs(n) - 1, out _, out _, out _);
return n < 0 && n.IsEven ? -fibonacci : fibonacci;
}
/// <summary>Calculates matrix power B = A^n of a 2x2 matrix.</summary>
/// <returns>b11</returns>
static Nat MatrixPower(
Nat a11, Nat a12, Nat a21, Nat a22, Nat n,
out Nat b12, out Nat b21, out Nat b22)
{
if (n == 0)
{
b12 = b21 = 0; return b22 = 1;
}
Nat c12, c21, c22, c11 = MatrixPower(
a11, a12, a21, a22,
n.IsEven ? n / 2 : n - 1,
out c12, out c21, out c22);
if (n.IsEven)
{
a11 = c11; a12 = c12; a21 = c21; a22 = c22;
}
b12 = c11 * a12 + c12 * a22;
b21 = c21 * a11 + c22 * a21;
b22 = c21 * a12 + c22 * a22;
return c11 * a11 + c12 * a21;
}
}
答案 2 :(得分:3)
Double数据类型的上限值为1.7 x 10 ^ 308
1474的计算包括一步的值~1.1 x 10 ^ 308。
所以,到1475年,你肯定超过了Double所能代表的。不幸的是,C#唯一更大的原语,十进制(一个128位数)被设计成具有非常高的进动但是具有相对小的范围(仅高达大约10 ^ 28)。
如果没有设计可以处理大于10 ^ 308且具有一定程度的小数精度的数字的自定义数据类型,我看不到这样做的方法。也就是说,那里的人可能已经上过这样的课程,因为我可以想象它可以派上用场。
见double:http://msdn.microsoft.com/en-us/library/678hzkk9(v=VS.80).aspx
和十进制:http://msdn.microsoft.com/en-us/library/364x0z75(v=VS.80).aspx
答案 3 :(得分:2)
'Solver Foundation' library似乎包含一些“大”数字类型。它的Rational
类型可能会为您提供所需的精度和范围。它表示理性为两个BigInteger
值的比率。 (它带来了自己的BigInteger
- 我猜它是在.NET 4发布之前编写的。)
理论上,它使它能够代表非常大的数字,但也代表了很高的精度。 (显然你的公式不涉及有理数,但浮点数也是这里的近似值。)
它提供了一种方法,可以将Rational
提升为其他内容的力量:http://msdn.microsoft.com/en-us/library/microsoft.solverfoundation.common.rational.power(v=VS.93).aspx
答案 4 :(得分:1)
正如您正确指出的那样,BigInteger没有实现Sqrt方法。 你可以自己实现它:
Calculate square root of a BigInteger (System.Numerics.BigInteger)
但是你的代码仍然存在精度问题。
答案 5 :(得分:1)
这里的许多答案表明复杂性可以最小化为O(log(n))。为什么不尝试log(n)方法的整数实现?
首先,请考虑从Fibonacchi的序列中给出两个术语:F(n)
和F(n+1)
。逻辑上,较大的术语F(n+k)
可以写为F(n)
和F(n+1)
的线性函数
F(n+k) = Ck1*F(n) + Ck2*F(n+1)
你可以只计算这些系数(仅取决于k
),(有趣的是,它们也是斐波那契序列!)并使用它们更快地前进,然后再次计算它们以获得更大的{{{{ 1}}能够更快地前进,等等。
答案 6 :(得分:1)
最快(最脏)? :d
private Double dirty_math_function(Int32 input){
Double part1 = (1 / Math.Sqrt(5));
Double part2 = Math.Pow(((1 + Math.Sqrt(5)) / 2), input);
Double part3 = Math.Pow(((1 - Math.Sqrt(5)) / 2), input);
return (part1 * part2) - (part1 * part3);
}
private Double FibSequence(Int32 input) {
if(input < 1475)
return dirty_math_function(input);
else{
return (FibSequence(input -1) + FibSequence(intput -2));
}
}
答案 7 :(得分:0)
问题是(5 ^(1/2)^ 1475)容易溢出int。你要做的是编写一个“大数学”库来处理从内存中进行数学运算(逐位)而不是使用硬类型数据类型。它是一种痛苦,但我知道。查找正方形和乘法方法。
答案 8 :(得分:0)