Q, 47 Bytes
m:{*/1_-':|(0<){y-x x bin y}[*+60(|+\)\1 0]\x}
Test
+(i;m'i:1 2 3 4 5 6 7 8 9 42 1000 12345)
read it as pairs (i,map(m,i)), where m is the calculating function and i the different args
writes
1 1
2 2
3 3
4 3
5 5
6 5
7 10
8 8
9 8
42 272
1000 12831
12345 138481852236
Explanation
n funtion\arg
applies function(function(function(...function(args))) n times (internally uses tal recursion), and returns the sequence of results. We calculate the 60 first items of fibonnaci series as *+60(|+\)\1 0
. In that case the function is (|+): +\ applied over a sequence calculates partial sums (ex +\1 2 3 is 1 3 6), and | reverses seq. So each 'iteration' we calculate partial sums of the two previous fibonacci number and return the partial sums reversed. 60(|+\)\1 0
generates sequences 1 0, 1 1, 2 1, 3 2, 5 3, 8 5, 13 8, 21 13, ... *+
applied over this result flip (traspose) it and takes the first. Result is sequence 1 1 2 3 5 8 13 21 34 55 ..
(cond)function\args
applies function(function(..function(args))) while cond true, and returns the sequence of partial results
function[arg]
applied over a function of more than one argument creates a projection (partial application)
We can give name to the args, but the implicit names are x,y,z
{y-x x bin y}[*+60(|+\)\1 0]
declares a lambda with args x,y with partial projection (arg x is fibonacci series, calculates as *+60(|+)\1 0). x represent fibonacci values, and y the number to process. Binary search (bin) is used to locate the index of the greater fibonacci number <=y (x bin y
), and substract the corresponding value of x.
To calculate product from partial resuls we reverse them and calculate difference of each pair (-':|
), drop the first (1_
because is 0) and multiply over (*/
).
If we are interested in accumulated sum the code is the same, but with +/
instead of */
. We can also use any other diadic operator instead of + or *
About execution efficiency
I know that in this contest efficiency is not an issue. But in this problem we can range from lineal cost to exponential cost, so i'm curious about it.
I developed a second version (length 48 Bytes excluding comment) and repeated test cases battery 1000 times on both versions.
f:*+60(|+\)\1 0;m:{*/1_-':|(0<){x-f f bin x}\x} /new version
execution time is: original version 0'212 seg, new version 0'037 seg
Original version calculates fibbonaci serie once per function application; new version calculates fibonacci just one.
In both cases calculation of fibonacci series uses tail recursion
6The statement isn't quite correct. E.g.
2
can be decomposed as-1 + 3
. The correct statement of Zeckendorf's theorem is that a positive Fibonacci number can be uniquely decomposed as a sum of non-consecutive Fibonacci numbers with positive index. – Peter Taylor – 2016-05-13T10:07:07.7371@PeterTaylor I don't consider negative Fibonacci numbers part of the series for this question. Consecutive or not only matters when you want the indices, we do not care about the indices for this question. – orlp – 2016-05-13T10:09:05.640
1I'm not saying that you should change the question to support negative Fibonacci numbers: I'm saying that you should edit it to be explicit about the assumptions which you're making. – Peter Taylor – 2016-05-13T11:03:44.453
1@orlp consecutive or not matters quite a bit, since two different forms would give two different products. You already stated the problem in a way that already implicitly rules out consecutive Fibonacci terms, though, so there's nothing to worry about there. – hobbs – 2016-05-13T20:44:06.483
2(specifically: F(n) and F(n+1) can't both appear in the output because the algorithm guarantees that, before considering them, the remainder is already less than F(n+2) = F(n) + F(n+1)) – hobbs – 2016-05-13T20:46:20.690