A Linear Algebra Trick for Computing Fibonacci Numbers Fast (codeconfessions.substack.com)
from abhi9u@lemmy.world to programming@programming.dev on 06 Nov 2023 14:19
https://lemmy.world/post/7876082

#programming

threaded - newest

[deleted] on 06 Nov 2023 16:03 next collapse
.
FlapKap@feddit.dk on 06 Nov 2023 16:14 collapse

According to the article the linear algebra algorithm has a running time of O(log n)

[deleted] on 06 Nov 2023 16:29 collapse
.
IAm_A_Complete_Idiot@sh.itjust.works on 06 Nov 2023 16:42 collapse

According to the benchmark in the article it’s already way faster at n = 1000. I think you’re overestimating the cost of multiplication relative to just cutting down n logarithmically.

log_2(1000) = roughly a growth factor of 10. 2000 would be 11, and 4000 would be 12. Logs are crazy.

cbarrick@lemmy.world on 06 Nov 2023 20:14 collapse

The article is comparing to the dynamic programming algorithm, which requires reading and writing to an array or hash table (the article uses a hash table, which is slower).

The naive algorithm is way faster than the DP algorithm.

t_veor@sopuli.xyz on 07 Nov 2023 01:28 collapse

It’s not that hard to check yourself. Running the following code on my machine, I get that the linear algebra algorithm is already faster than the naive algorithm at around n = 100 or so. I’ve written a more optimised version of the naive algorithm, which is beaten somewhere between n = 200 and n = 500.

Try running this Python code on your machine and see what you get:

import timeit

def fib_naive(n):
    a = 0
    b = 1
    while 0 < n:
        b = a + b
        a = b - a
        n = n - 1
    return a

def fib_naive_opt(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b + a, b
    return a

def matmul(a, b):
    return (
        (a[0][0] * b[0][0] + a[0][1] * b[1][0], a[0][0] * b[0][1] + a[0][1] * b[1][1]),
        (a[1][0] * b[0][0] + a[1][1] * b[1][0], a[1][0] * b[0][1] + a[1][1] * b[1][1]),
    )

def fib_linear_alg(n):
    z = ((1, 1), (1, 0))
    y = ((1, 0), (0, 1))
    while n > 0:
        if n % 2 == 1:
            y = matmul(y, z)
        z = matmul(z, z)
        n //= 2

    return y[0][0]

def time(func, n):
    times = timeit.Timer(lambda: func(n)).repeat(repeat=5, number=10000)
    return min(times)

for n in (50, 100, 200, 500, 1000):
    print("========")
    print(f"n = {n}")
    print(f"fib_naive:\t{time(fib_naive, n):.3g}")
    print(f"fib_naive_opt:\t{time(fib_naive_opt, n):.3g}")
    print(f"fib_linear_alg:\t{time(fib_linear_alg, n):.3g}")

Here’s what it prints on my machine:

========
n = 50
fib_naive:      0.0296
fib_naive_opt:  0.0145
fib_linear_alg: 0.0701
========
n = 100
fib_naive:      0.0652
fib_naive_opt:  0.0263
fib_linear_alg: 0.0609
========
n = 200
fib_naive:      0.135
fib_naive_opt:  0.0507
fib_linear_alg: 0.0734
========
n = 500
fib_naive:      0.384
fib_naive_opt:  0.156
fib_linear_alg: 0.112
========
n = 1000
fib_naive:      0.9
fib_naive_opt:  0.347
fib_linear_alg: 0.152
cbarrick@lemmy.world on 07 Nov 2023 16:41 collapse

Nice.

I have successfully stuck my foot in my own mouth.

spudwart@spudwart.com on 06 Nov 2023 19:27 next collapse

Sweet, Sweet, Linear Algebra

<img alt="" src="https://media.tenor.com/iNNUYSTCCjYAAAAC/spongebob-hate.gif">

germanatlas@lemmy.blahaj.zone on 07 Nov 2023 22:29 next collapse

I’ll just stick with phi, thank you

DontAskAboutUpdog@lemmy.world on 10 Nov 2023 19:50 collapse

How could we live without this?