• Search
  • Blog
  • About
  • <Escape />

    Stay hungry. Stay foolish.

    How to calculate nth fibonacci number?

    How would you write a program to calculate a given Fibonacci number, say 50th and 500th?

    1. Use Dynamic Programming with Space Optimized

    size_t fibonacci(int n) {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
    
        size_t a = 0;
        size_t b = 1;
        size_t c = a + b;
    
        for (int i = 2; i <= n; i++) {
            c = a + b;
            a = b;
            b = c;
        }
    
        return c;
    }
    
    • Time Complexity: O(n)
    • Extra Space: O(1)

    2. Use Power of Matrix

    Because \(\begin{align*} \left(\begin{array}{ccc} F_{n}, & F_{n-1} \\ F_{n-1}, & F_{n-2} \end{array} \right) \left(\begin{array}{ccc} 1, & 1 \\ 1, & 0 \end{array} \right) = \left(\begin{array}{ccc} F_{n} + F_{n-1}, & F_{n} \\ F_{n-1} + F_{n-2}, & F_{n-1} \end{array} \right) = \left(\begin{array}{ccc} F_{n+1}, & F_{n} \\ F_{n}, & F_{n-1} \end{array} \right) \end{align*}\) , therefore \(\begin{align*} F_{n+1} = \left(\begin{array}{ccc} 1, & 1 \\ 1, & 0 \end{array} \right)^n \end{align*}\)

    class Matrix2i {
    public:
        Matrix2i(const size_t& m00, const size_t& m01,
            const size_t& m10, const size_t& m11) {
            m_data[0][0] = m00;
            m_data[0][1] = m01;
            m_data[1][0] = m10;
            m_data[1][1] = m11;
        }
    
        const size_t* operator[](const size_t& i) const {
            return m_data[i];
        }
    
        Matrix2i& multiply(const Matrix2i& other) {
            auto m00 = m_data[0][0] * other[0][0] + m_data[0][1] * other[1][0];
            auto m01 = m_data[0][0] * other[0][1] + m_data[0][1] * other[1][1];
            auto m10 = m_data[1][0] * other[0][0] + m_data[1][1] * other[1][0];
            auto m11 = m_data[1][0] * other[0][1] + m_data[1][1] * other[1][1];
    
            m_data[0][0] = m00;
            m_data[0][1] = m01;
            m_data[1][0] = m10;
            m_data[1][1] = m11;
    
            return *this;
        }
    
        Matrix2i& pow(size_t n) {
            if (n == 0 || n == 1) {
                return *this; // mathmatical incorrect when n == 0
            }
    
            Matrix2i copy(*this);
    
            pow(n / 2).multiply(*this);
    
            if (n % 2 == 1) {
                multiply(copy);
            }
    
            return *this;
        }
    
    private:
        size_t m_data[2][2];
    };
    
    size_t fibonacci(size_t n) {
        if (n == 0) {
            return 0;
        }
    
        Matrix2i m(1, 1, 1, 0);
        return m.pow(n - 1)[0][0];
    }
    
    • Time Complexity: O(log(n))
    • Extra Space: O(log(n)) if considering function call stacks, otherwise O(1).

    3. Use Template Metaprogramming Supported in C++11

    • Loops with recursive template definitions
    • Conditionals with partial template specializations
    • Returns using typedefs
    // solution 1
    template<size_t N>
    struct fibonacci {
        static const size_t cValue = fibonacci<N-1>::cValue + fibonacci<N-2>::cValue;
    };
    
    template<>
    struct fibonacci<0> {
        static const size_t cValue = 0;
    };
    
    template<>
    struct fibonacci<1> {
        static const size_t cValue = 1;
    };
    
    // solution 2
    template<size_t N>
    struct fibonacci: integral_constant<size_t, fibonacci<N-1>::value + fibonacci<N-2>::value> {};
    
    template<> struct fibonacci<0>: integral_constant<size_t, 0> {};
    template<> struct fibonacci<1>: integral_constant<size_t, 1> {};
    
    • Time Complexity: O(1)

    Performance Comparision When N is 50

    Method Microseconds Ticks
    O(n) 28 28200
    O(log(n)) 1 1700
    O(1) 0 700

    However, when N is 500, integers will overflow during computation. Hence we need a BigInt class

    #include <vector>
    using namespace std;
    
    /**
     * Non-negative big integers with operator + supported
     */
    class BigInt {
    public:
        BigInt(size_t i) {
            m_digits.clear();
            while (i > 0) {
                m_digits.push_back(i % 10);
                i = i / 10;
            }
        }
    
        /**
         * friends defined inside class body are inline and are hidden from non-ADL lookup,
         * passing lhs by value helps optimize chained a+b+c
         */
        friend BigInt operator+ (BigInt lhs, const BigInt& rhs) {
            auto p = lhs.m_digits.begin();
            auto q = rhs.m_digits.begin();
    
            int carry = 0;
            while (q != rhs.m_digits.end()) {
                if (p == lhs.m_digits.end()) {
                    lhs.m_digits.push_back(0);
                    p = lhs.m_digits.end() - 1;
                }
    
                *p += *q + carry;
                carry = *p / 10;
                *p %= 10;
    
                p++;
                q++;
            }
    
            while (carry) {
                lhs.m_digits.push_back(carry);
                carry = carry / 10;
            }
    
            return lhs;
        }
    
        friend ostream& operator<< (ostream& os, const BigInt& bigInt) {
            for (int i = bigInt.m_digits.size() - 1; i >= 0; i--) {
                os << static_cast<int>(bigInt.m_digits[i]);
            }
        }
    
    private:
        vector<char> m_digits;
    };
    

    And a fixed version of the previous dynamic programming solution

    template<typename T>
    T fibonacci1(int n) {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
    
        T a = 0;
        T b = 1;
        T c = a + b;
    
        for (int i = 2; i <= n; i++) {
            c = a + b;
            a = b;
            b = c;
        }
    
        return c;
    }
    

    Result: 1394232245616978801397243828704072839500702565876973072641089629483255 71622863290691557658876222521294125 in 3157 microseconds(3157921 ticks).

    Read