Note that most examples of this sequence generator have a nasty bug in them. Most examples use the int data type, whilst this code uses the long data type, otherwise this function overflows around n=47.
The naive way to implement this is recursively:
/// <summary> /// slow version of fib calc for testing - warning /// runs in O(1.618..n) /// </summary> internal long Fibonacci(int n) { if (n < 2) return n; else return Fibonacci(n - 1) + Fibonacci(n - 2); }
And a more optimal approach is:
/// <summary> /// returns the Fibonacci number for N /// </summary> public long Fibonacci(int n) { // returns N where n < 2, for higher values of N // avoids recursive solution by keeping track // of previous results long fn = 0; if (n < 0) // offset must be positive throw new NotSupportedException("Fibonacci sequence offset must be positive"); else if (n < 2) // returns N where N < 2 fn = n; else // use caching technique for higher values of N { long fnMinusTwo = 0; long fnMinusOne = 1; for (long ni = 2; ni <= n; ni++) { // effectively: fn = Fibonacci(n - 1) + Fibonacci(n - 2) fn = fnMinusOne + fnMinusTwo; fnMinusTwo = fnMinusOne; fnMinusOne = fn; } } return fn; }The latter approach is much more efficient O(n) vs O(1.618..n). However its not as simple as the former. Another approach is to take the former solution and use a local cache:
/// <summary> /// caching version of fib calc /// </summary> Dictionary<int, long> fibCache = new Dictionary<int, long>(); internal long Fibonacci(int n) { long fn = 0; // use value from cache if already calculated if (fibCache.ContainsKey(n)) return fibCache[n]; // not cached, need to work it out if (n < 2) fn = n; else fn = Fibonacci(n - 1) + Fibonacci(n - 2); // add to cache fibCache.Add(n, fn); return fn; }This gives us an O(n) runtime with a reasonably simple implementation. This is of course the classic trade-off of time vs. space - we have increased space used (the dictionary) to decrease time.
Finally we want to generate the whole sequence, which we can do in a lazy evaluation way using the yield keyword:
/// <summary> /// returns the first N items in the Fibonacci sequence /// </summary> public static IEnumerable<long> FibonacciSequence(int n) { for (int fn = 0; fn < n; fn++) yield return Fibonacci(fn); }
No comments:
Post a Comment