Q, 47 bytes
m:{*/1_-':|(0<){y-x x bin y}[*+60(|+\)\1 0]\x}
Prueba
+(i;m'i:1 2 3 4 5 6 7 8 9 42 1000 12345)
léalo como pares (i, map (m, i)), donde m es la función de cálculo e i los diferentes argumentos
escribe
1 1
2 2
3 3
4 3
5 5
6 5
7 10
8 8
9 8
42 272
1000 12831
12345 138481852236
Explicación
n funtion\arg
aplica function (function (function (... function (args))) n veces (usa internamente recursión tal) y devuelve la secuencia de resultados. Calculamos los 60 primeros elementos de la serie fibonnaci como *+60(|+\)\1 0
. En ese caso, la función es ( | +): + \ aplicado sobre una secuencia calcula sumas parciales (por ejemplo, + \ 1 2 3 es 1 3 6) e invierte la secuencia. Por lo tanto, cada 'iteración' calculamos sumas parciales de los dos números de Fibonacci anteriores y devuelve el parcial sumas invertidas. 60(|+\)\1 0
genera secuencias 1 0, 1 1, 2 1, 3 2, 5 3, 8 5, 13 8, 21 13, ... *+
aplicadas sobre este resultado, voltéalo (traspónsalo) y toma el primero. El resultado es la secuencia 1 1 2 3 5 8 13 21 34 55 ..
(cond)function\args
aplica function (function (.. function (args))) mientras cond verdadero, y devuelve la secuencia de resultados parciales
function[arg]
aplicado sobre una función de más de un argumento crea una proyección (aplicación parcial)
Podemos dar nombre a los argumentos, pero los nombres implícitos son x, y, z
{y-x x bin y}[*+60(|+\)\1 0]
declara una lambda con args x, y con proyección parcial (arg x es una serie de Fibonacci, calcula como * + 60 (| +) \ 1 0). x representa los valores de Fibonacci, yy el número a procesar. La búsqueda binaria (bin) se usa para ubicar el índice del mayor número de Fibonacci <= y ( x bin y
) y restar el valor correspondiente de x.
Para calcular el producto a partir de resultados parciales, los invertimos y calculamos la diferencia de cada par ( -':|
), soltamos el primero ( 1_
porque es 0) y lo multiplicamos ( */
).
Si estamos interesados en la suma acumulada, el código es el mismo, pero con en +/
lugar de */
. También podemos usar cualquier otro operador diadic en lugar de + o *
Sobre la eficiencia de ejecución
Sé que en este concurso la eficiencia no es un problema. Pero en este problema podemos variar desde el costo lineal hasta el costo exponencial, así que tengo curiosidad al respecto.
Desarrollé una segunda versión (48 bytes de longitud sin incluir comentarios) y repetí la batería de casos de prueba 1000 veces en ambas versiones.
f:*+60(|+\)\1 0;m:{*/1_-':|(0<){x-f f bin x}\x} /new version
el tiempo de ejecución es: versión original 0'212 seg, nueva versión 0'037 seg
La versión original calcula la serie fibbonaci una vez por aplicación de función; La nueva versión calcula Fibonacci solo uno.
En ambos casos, el cálculo de la serie de Fibonacci utiliza la recursión de la cola
2
se puede descomponer como-1 + 3
. La afirmación correcta del teorema de Zeckendorf es que un número positivo de Fibonacci puede descomponerse de manera única como una suma de números de Fibonacci no consecutivos con índice positivo.