Search code examples
pythonfibonaccifloating-accuracy

Errors in Directly vs Recursively Calculating a given Fibonacci Number


I was bored at work and was playing with some math and python coding, when I noticed the following:

Recursively (or if using a for loop) you simply add integers together to get a given Fibonacci number. However there is also a direct equation for calculating Fibonacci numbers, and for large n this equation will give answers that are, frankly, quite wrong with respect to the recursively calculated Fibonacci number.

I imagine this is due to rounding and floating point arithmetic ( sqrt(5) is irrational after all), and if so can anyone point me into a direction on how I could modify the fibo_calc_direct function to return a more accurate result?

Thanks!

def fib_calc_recur(n, ii = 0, jj = 1): 
    #n is the index of the nth fibonacci number, F_n, where F_0 = 0, F_1 = 1, ...
    if n == 0: #use recursion
        return ii
    if n == 1:
        return jj
    else:
        return(fib_calc_recur(n -1, jj, ii + jj))

def fib_calc_direct(n):

    a = (1 + np.sqrt(5))/2
    b = (1 - np.sqrt(5))/2

    f = (1/np.sqrt(5)) * (a**n - b**n)
    return(f)

Solution

  • You could make use of Decimal numbers, and set its precision depending on the magninute of n

    Not your question, but I'd use an iterative version of the addition method. Here is a script that makes both calculations (naive addition, direct with Decimal) for values of n up to 4000:

    def fib_calc_iter(n): 
        a, b = 0, 1
        
        if n < 2:
            return n
        for _ in range(1, n):
            a, b = b, a + b
        return b
    
    from decimal import Decimal, getcontext
    
    def fib_calc_decimal(n):
        getcontext().prec = n // 4 + 3  # Choose a precision good enough for this n
        sqrt5 = Decimal(5).sqrt()
        da = (1 + sqrt5) / 2
        db = (1 - sqrt5) / 2
        f = (da**n - db**n) / sqrt5
        return int(f + Decimal(0.5))  # Round to nearest int
    
    # Test it...
    for n in range(1, 4000):
        x = fib_calc_iter(n)
        y = fib_calc_decimal(n)
        if x != y:
            print(f"Difference found for n={n}.\nNaive method={x}.\nDecimal method={y}")
            break
    else:
        print("No differences found")