3

The code below is able to determine the correct sequence up to a point namely 70 using the data type unsigned long long. I know the sequence can become large thus I mod 10,000 the results. I want to determine the nth term for 10,000 using the best data type or improve the algo to calculate the nth term.

#define MOD %10000

unsigned long long calc(long nth) {
    return (pow( 1 + sqrt(5), nth ) - pow( 1 - sqrt(5), nth )) / (pow(2.0, nth)*(sqrt(5)));
}

int main() {
    long t, nth;
    for (std::cin>>t;  t-- && std::cin>>nth; ) {
        std::cout<<calc(nth-2)MOD<<" "<<calc(nth-1)MOD<<" "<<calc(nth)MOD<<std::endl;
    }   
    return 0;
}
Nayuki
  • 17,911
  • 6
  • 53
  • 80
Mohalland
  • 35
  • 7
  • 12
    Stylistically - the use of `#define MOD %10000` is really not a good idea. It makes the code significantly harder to read and much more error-prone. – templatetypedef Feb 07 '14 at 19:10

2 Answers2

12

Your algorithm will not compute the correct result for large N's, due to the floating point errors of sqrn(5).

In order to speed up your algorithm you can use fast doubling Fibonacci:

   F(2k) = F(k)[2F(k+1) - F(k)]
   F(2k+1) = F(k+1)^2 + F(k)^2

Applying modulo arithmetics, your final fastest algorithm would be:

   F(2k) = F(k)[2F(k+1) - F(k)] % 10000
   F(2k+1) = (F(k+1)^2 + F(k)^2) % 10000

Using this approach, your function never exceeds 10000, thus an int type suffices.

EDIT: Okay I had some free time on a Friday night (not a good thing I guess) and implemented the algorithm. I implemented two versions, first one with O(1) memory and O(lg n) time complexity and second one using a cache, with memory and worst-case runtime of O(lg n), but with a best case runtime of O(1).

#include <iostream>
#include <unordered_map>

using namespace std;

const int P = 10000;

/* Fast Fibonacci with O(1) memory and O(lg n) time complexity. No cache. */

int fib_uncached (int n)
{
    /* find MSB position */
    int msb_position = 31;
    while (!((1 << (msb_position-1) & n)) && msb_position >= 0)
        msb_position--;

    int a=0, b=1; 

    for (int i=msb_position; i>=0;i--)
    {       
        int d = (a%P) * ((b%P)*2 - (a%P) + P),
            e = (a%P) * (a%P) + (b%P)*(b%P);
        a=d%P;
        b=e%P;

        if (((n >> i) & 1) != 0)
        {
            int c = (a + b) % P;
            a = b;
            b = c;
        }
    }
    return a;
}  

/* Fast Fibonacci using cache */
int fib (int n)
{
    static std::unordered_map<int,int> cache;

    if (cache.find(n) == cache.end()) 
    {
        int f;
        if (n==0)
            f = 0;
        else if (n < 3)
            f = 1;
        else if (n % 2 == 0)
        {
            int k = n/2;
            f = (fib(k) * (2*fib(k+1) - fib(k))) % P;
        } 
        else
        {
            int k = (n-1)/2;
            f = (fib(k+1)*fib(k+1)+ fib(k) * fib(k)) % P;
        }
        if (f<0)
            f += P;

        cache[n] = f;
    }
    return cache.at(n);
}

int main ()
{
    int i ;
    cin >> i;
    cout << i << " : " << fib(i) << endl;
return 0;
}

Reference for cache-less implementations: https://www.nayuki.io/page/fast-fibonacci-algorithms

Nayuki
  • 17,911
  • 6
  • 53
  • 80
Ali Alavi
  • 2,367
  • 2
  • 18
  • 22
  • Actually, even `Fm(n) = (Fm(n-1) + Fm(n-2)) % 10000`copes with `int` becaus eno intermediate result exceeds 20000. (On second reading I guess you maybe meant all $F$ to stand for the "true" Fibonacchi numbers throughout, not the reduced ones?) – Hagen von Eitzen Feb 07 '14 at 19:19
  • This is approx O(log(n)) space and time. n is divided by 2 at each call -> so '2^calls = n'. At each calls it is space/stack increased. – Luka Rahne Feb 07 '14 at 21:52
  • Yes, changing from `map` to `unordered_map` drops the `log(P)` from the time (easy to disagree when you can change the topic :). And yes, for repeated calls, given that the cache is already set, a single call can be O(1). But that would be like me saying that the last iteration of my `for` loop is O(1). – Matt Feb 07 '14 at 21:58
  • +1 I reversed my previous -1 since I agree with @LukaRahne - this is a O(log(n)) algorithm. By counting the number of times `fib` is called, I only got 203 for n=10 million and 109 for n=10 thousand. – Matt Feb 07 '14 at 22:03
  • I answered a related question in this link :http://stackoverflow.com/questions/16388982/algorithm-function-for-fibonacci-series/16389221#16389221 – amin k Mar 03 '14 at 10:04
0

Calculate the terms successively, taking the mod at each step. Since each term only depends on the previous two, your computational space is just a 3-element array.

#include <iostream>
using namespace std;

typedef unsigned long long numtype;

const numtype MOD = 10000;

// Assume n is 1-based

numtype fib(int n)
{
  numtype seq[3] = {1,1,2};
  if( --n < 3 ) return seq[n]; // make n 0-based
  for( int i=3 ; i<=n ; ++i )
  {
    seq[i%3] = (seq[(i-1)%3] + seq[(i-2)%3]) % MOD;
    cout << seq[i%3] << ' '; // comment out for large n
  }
  return seq[n%3];
}

int main()
{
//numtype answer = fib(10000000); // 6875
  numtype answer = fib(70); // 9135
  cout << endl;
  cout << "answer = " << answer << endl;
}

Output:

3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 946 7711 8657 6368 5025 1393 6418 7811 4229 2040 6269 8309 4578 2887 7465 352 7817 8169 5986 4155 141 4296 4437 8733 3170 1903 5073 6976 2049 9025 1074 99 1173 1272 2445 3717 6162 9879 6041 5920 1961 7881 9842 7723 7565 5288 2853 8141 994 9135

(The first 3 terms 1,1,2 are intentionally missing.) The 70th term is 9135.

Time is O(n) and memory is O(1).

Matt
  • 20,108
  • 1
  • 57
  • 70
  • 1
    @Lazarus Printing out the series just demonstrates the sanity of it. Simply comment out the `cout` line if you don't want to see that. To get F(n), this is the return value of `fib`. For instance, `fib(10000000)==626` which runs about the same time as yours. Is this also the value you found for 10 000 000? – Matt Feb 07 '14 at 20:54
  • @Lazarus The 10 millionth term is 6875. The previous term is 626; sorry. – Matt Feb 07 '14 at 21:21
  • No, my program returns fib(10 000 000) = 6875. Though there is no way that I can check it. Do you know any online fibonacci calculator for large numbers? – Ali Alavi Feb 07 '14 at 21:22
  • I just checked Mathematica and it took a 2+Mbyte file to hold the answer, but the last 4 digits are in fact 6875. – Matt Feb 07 '14 at 21:31
  • 1
    Great solution Matt, the size issue was solved. Is there a was to further improve the efficiency to run under 5000(ms)? – Mohalland Feb 10 '14 at 20:56
  • @Mohalland Yes, actually in my opinion I would recommend choosing Lazarus's answer since it is faster. – Matt Feb 10 '14 at 21:00