As a person with mathematical training, and not with CS, I feel obligated to mention this, while the Said Amiri algorithm is very good and will probably work fast enough for N up to several million (with permanent memory, of course) is the best algorithm in terms of time.
I'll pick up where he left:
f(n+1) = f(n) + g(n-2) g(n+1) = f(n) + g(n)
Since f and g are discrete functions, you can consider them as sequences. Then it becomes a linear system of recurrence relations. Fortunately, such a system can be completely solved, so that we can imagine the explicit form of f and g.
Unfortunately, SO doesn't seem to support MathJax like math.SE, so I apologize for the poor quality of the equations from here.
Let be
| f (n) |
| f (n-1) |
u (n) = | f (n-2) |
| g (n) |
| g (n-1) |
| g (n-2) |
That is, u (n) is a vector string. Then the following is true:
| f (n + 1) | | 1 0 0 0 0 1 | | f (n) |
| f (n) | | 1 0 0 0 0 0 | | f (n-1) |
| f (n-1) | = | 0 1 0 0 0 0 | . | f (n-2) |
| g (n + 1) | | 1 0 0 1 0 0 | | g (n) |
| g (n) | | 0 0 0 1 0 0 | | g (n-1) |
| g (n-1) | | 0 0 0 0 1 0 | | g (n-2) |
It follows that u(n) = A * u(n-1)
, where A is the matrix above.
Then u(n) = (A^(n-2)) * u(2)
, where u(2)
is the vector containing the initial values ββfor the problem. This, in turn, gives the O(log(n))
complexity algorithm, since you can use fast exposure to compute (A^(n-2))
, and then multiply it by u(2)
.
Of course, any such method is likely to require BigInt, since otherwise overflow is almost guaranteed.
Also note that this method can be applied one more step:
You can find the eigenvectors and eigenvalues ββof A, and then decompose u(2)
into eigenvectors. Then you will have a closed form for both f (n) and g (n).
I highly recommend you use a closed-form algorithm
This will almost certainly include high-precision floating-point calculations (if all eigenvalues ββare not integers, which is unlikely), which at least have such programming complexity and are not usually constant-time operations. Of course, BigInt operations are not performed. Thus, a constant-time algorithm is usually not possible, plus you probably don't even need O(log(n))
, since linearity is good enough for most applications.
Note
The technique described here can be used in many problems and is extremely useful in dynamic optimization problems. Plus, usually people are very impressed when they see it for the first time;)