我收到了这个很好的非递归函数,用于计算斐波那契序列。
因此,我编写了一些 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);
}
而且,不,这不是家庭作业。对于缓慢的一天来说,这只是一个“简单”的问题。
I was sent this nice non-recursive function for computing a fibonacci sequence.
So I coded up a bit of c# and was able to verify all numbers up to 1474 were correct.
The problem comes in when attempting to calculate it for 1475 and above. My c# math skills just aren't up to the task of figuring out a different way. So, does someone have a better way of expressing this particular math function in c#? other than the traditional way of doing a recursive function?
Incidentally, I started to use BigInteger as the return type. But the problem really comes in when trying to raise (1+Math.Sqrt(5) /2) to the 1475th power. I just don't see what data type I need (nor mechanism for that matter) to get this to come back with something other than Infinity.
Here's a starting point.
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);
}
And, no, it's not homework. Just a "simple" problem for a slow day.
发布评论
评论(9)
我认为 C# 没有一个具有足够浮动精度和范围的数据类型来简单地处理这个问题。
如果您确实想走这条路,您可以注意到共轭 小于 1,因此 与舍入到最接近的整数相同,因此您可以简化解决方案以查找 。然后使用二项式展开,这样您只需计算 ,带有适当的 a 和 b (它们是有理数,可以计算与 BigInteger 完全相同)。如果你仍然回到 Double ,你仍然不会比 1475 更远,但你应该能够弄清楚如何仅使用精确的整数数学来完成这部分 ☺
还有另一种计算斐波那契数的巧妙方法,即使用矩阵求幂:
如果你聪明的话,这可以在 O(log n) 内完成。
我最终在 Haskell 中实现了这些。
fib1
是矩阵求幂,fib2
是封闭式公式的精确整数转换,如上所述。它们各自的运行时如下所示,由 Criterion 测量://www.haskell.org/ghc/download_ghc_7_0_3" rel="nofollow noreferrer">GHC 7.0.3:I don't think C# has a data type with enough floating precision and range to handle this naïvely.
If you really want to go down this path, you can notice that the conjugate is less than one, so does the same as rounding to the nearest integer, thus you can simplify your solution to finding . Then use binomial expansion so that you only have to calculate , with appropriate a and b (which are rational and can be computed exactly with BigInteger). If you still go back to Double for this, you still won't get much further than 1475, but you should be able to figure out how to do even this part with exact integer math only ☺
There's another neat method for computing Fibonacci numbers, using matrix exponentiation:
This can be done in O(log n) if you're clever.
I ended up implementing these in Haskell.
fib1
is matrix exponentiation andfib2
is exact integer translation of the closed-form formula, as described above. Their respective runtimes look like this, as measured by Criterion when compiled by GHC 7.0.3:Double 数据类型的上限为 1.7 x 10^308。1474
的计算一步包含 ~ 1.1 x 10^308 的值。
因此,到 1475 年,您肯定超出了 Double 所能代表的范围。不幸的是,C# 唯一较大的原语 Decimal(128 位数字)被设计为具有非常高的进动,但范围相对较小(最多仅约 10^28)。
如果没有设计一个可以处理大于 10^308 且具有一定小数精度的数字的自定义数据类型,我看不到有什么方法可以做到这一点。也就是说,那里的人可能已经开设了这样的课程,因为我可以想象它可以派上用场的场景。
请参阅双重: http://msdn.microsoft.com /en-us/library/678hzkk9(v=VS.80).aspx
和十进制:http://msdn.microsoft.com/en-us/library/364x0z75(v=VS.80).aspx
The Double data type has an upper value limit of 1.7 x 10^308
The calculation for 1474 includes a value of ~ 1.1 x 10^308 at one step.
So, by 1475 you're definitely exceeding what Double can represent. Unfortunately, C#'s only larger primitive, Decimal (a 128-bit number) is designed to have very high precession but with relatively small range (only up to around 10^28).
Without designing a custom data type that can handle numbers larger than 10^308 with some degree of decimal precision, I don't see a way to do this. That said, someone out there may have already made such a class, as I can imagine scenarios where it could come in handy.
See double: http://msdn.microsoft.com/en-us/library/678hzkk9(v=VS.80).aspx
and decimal: http://msdn.microsoft.com/en-us/library/364x0z75(v=VS.80).aspx
将出现“Solver Foundation”库包括一些“大”数字类型。它的
Rational
类型可能会为您提供所需的精度和范围。它将有理数表示为两个 BigInteger 值的比率。 (它带来了自己的 BigInteger - 我猜它是在 .NET 4 发布之前编写的。)理论上,这使其能够表示非常大的数字,而且还能够表示很高的精度。 (显然你的公式不处理有理数,但浮点在这里也是一个近似值。)
它提供了一种将
Rational
提升到其他值的幂的方法:http://msdn.microsoft.com/ en-us/library/microsoft.solverfoundation.common.rational.power(v=VS.93).aspxThe 'Solver Foundation' library appears to include some 'big' number types. It's possible that its
Rational
type might provide you with the precision and range that you need. It represents a rational as a ratio of twoBigInteger
values. (It brings its ownBigInteger
- I guess it was written before .NET 4 shipped.)In theory, that enables it to represent very large numbers, but also to represent a great deal of precision. (Obviously your formula doesn't deal with rationals, but then floating point is also an approximation here.)
It offers a method for raising a
Rational
to the power of something else: http://msdn.microsoft.com/en-us/library/microsoft.solverfoundation.common.rational.power(v=VS.93).aspx正如您正确指出的那样,没有为 BigInteger 实现 Sqrt 方法。
不过,您可以自己实现它:
计算 BigInteger 的平方根 ( System.Numerics.BigInteger)
不过,精度仍然是您的代码的问题。
As you correctly pointed out, there is no Sqrt method implemented for BigInteger.
You can implement it yourself though:
Calculate square root of a BigInteger (System.Numerics.BigInteger)
The precision should still be a problem with your code though.
这里的许多答案表明复杂性可以最小化到 O(log(n))。为什么不尝试 log(n) 方法的整数实现?
首先,考虑斐波那契数列中的两项:
F(n)
和F(n+1)
。按照逻辑,较大的项F(n+k)
可以写成F(n)
和F(n+1)
的线性函数code> 因为您可以只计算这些系数(仅取决于
k
),(有趣的是,它们也是斐波那契数列!)并使用它们来更快地前进,然后再次计算它们以获得更大的值k
能够进步得更快,等等。Many answers here suggest that the complexity could be minimized to O(log(n)). Why not try an integer implementation of the log(n) approach?
First, consider that you are given two terms from Fibonacchi's sequence:
F(n)
andF(n+1)
. It's logic that the larger termsF(n+k)
can be written as a linear function ofF(n)
andF(n+1)
asYou could just compute these coefficients (that will depend only on
k
), (interestingly, they are a Fibonacci sequence too!) and use them to advance faster, then compute them again for larger values ofk
to be able to advance even faster, and so on.最快(也是最脏)的? :D
The quickest (and dirtiest) ? :D
问题是 (5^(1/2)^1475) 很容易溢出 int。您所要做的就是编写一个“大数学”库来处理从内存中(逐位)进行数学运算,而不是使用硬类型数据类型。这是一个痛苦,但是,我知道。查找平方和乘法。
the problem is that (5^(1/2)^1475) easily overflows int. What you have to do is write a 'big math' library to handle doing math from memory (bit for bit) rather than using the hard typed data types. its a pain in the but, i know. Look up the square and multiply method.