¡El desafío mínimo de Fibonacci!


19

Desafío

En esta tarea, se le dará un número entero N (menos de 10 6 ), encuentre la forma mínima en que podría sumar N usando solo números de Fibonacci; esta partición se llama representación de Zeckendorf .

Puede usar cualquier número de Fibonacci más de una vez y si hay más de una salida de representación.

Por ejemplo, si la entrada es 67, entonces una salida posible podría estar usando los números de Fibonacci 1,3,8,55, que también es el número mínimo de números de Fibonacci que podrían usarse para obtener la suma 67 .

La entrada N se da en una sola línea, las entradas son terminadas por EOF.

Ejemplos

Dado en el formato input: output

0: 0
47: 34+13
3788: 2584+987+144+55+13+5
1646: 1597+34+13+2
25347: 17711+6765+610+233+21+5+2
677: 610+55+8+3+1
343: 233+89+21
3434: 2584+610+233+5+2

Restricciones

  • El número de entradas no excedería los 10 6 valores.
  • Su programa no debe ejecutar más de 5 segundos para todas las entradas.
  • Puede usar cualquier idioma de su elección.
  • ¡La solución más corta gana!

"Podrías cualquier número de Fibonacci ..." ¿eh? "El número de entradas no excedería los 10 ^ 6 valores". Entonces, ¿nunca necesitaremos sumar más de 10 ^ 6 números? ¿Quiere decir que la suma de las entradas no excedería 10 ^ 6?
mellamokb

77
Spoilers: 1) El algoritmo codicioso (restar el mayor número de Fibonacci hasta que la entrada sea cero) produce soluciones óptimas. 2) Una solución óptima no necesita usar un número de Fibonacci dos veces (que se deduce de 1). 3) Una solución óptima, para N <= 1000000, no tendrá más de 14 términos.
Joey Adams

66
@Joey: de manera más general, el algoritmo codicioso descompone los enteros positivos en sumas de distintos números de Fibonacci de modo que no se usan números consecutivos de Fibonacci (esto se llama el teorema de Zeckendorf).
Nabb

1
Spoiler 4: 29 términos de la secuencia de Fibonacci que comienzan en 0 1 es suficiente.
Peter Taylor

@Nabb: Gracias por explicar la parte de matemáticas.
Quijotesco

Respuestas:


16

Ensamblaje Motorola 68000 - 34 bytes

(Sintaxis del ensamblador GNU)

| short min_fib_partition(long N asm("%d2"), long *out asm("%a0"))
min_fib_partition:
    | Generate Fibonacci numbers on the stack (-1, 1, 0, 1, 1, 2, 3, ..., 1134903170).
    moveq #-1, %d0          | A = -1
    moveq #1, %d1           | B = 1
generate_loop:
    move.l %d0, -(%sp)      | Push A to the stack.
    exg.l %d0, %d1          | A' = B
    add.l %d0, %d1          | B' = A + B
    bvc.s generate_loop     | Stop when signed long overflows.

    | Go back up the stack, partitioning N using the greedy algorithm.
    moveq #0, %d0           | Initialize return value (number of terms).
subtract_loop:
    move.l (%sp)+, %d1      | Pop a Fibonacci number F off the stack.
    cmp.l %d1, %d2          | If N < F, continue to smaller Fibonacci number.
    blt.s subtract_loop
    addq.w #1, %d0          | Increment the term count.
    move.l %d1, (%a0)+      | Append F to the output array.
    sub.l %d1, %d2          | N -= F
    bne.s subtract_loop     | Continue if N has not yet reached zero.

    | Clear the stack by searching for that -1.
clear_stack_loop:
    tst.l (%sp)+
    bge clear_stack_loop

done:
    rts

36 → 34: Se hizo que el generador de Fibonacci se detuviera en el desbordamiento en lugar de contar, y arregló el 0caso para que salga en [0]lugar de hacerlo []. Sin embargo, pasar un Naccidente negativo ahora.

El comentario en la parte superior es el prototipo C de esta función, utilizando una extensión de lenguaje para identificar qué parámetros van a dónde (de manera predeterminada, van en la pila).

Mi TI-89 , con su procesador de 10MHz, tarda 5 minutos en ejecutar esta función en 1 - 1,000,000.

Aunque el código de la máquina es (actualmente) menos bytes que la solución GolfScript, probablemente sería injusto aceptar esto como la solución más corta porque:

  • El código de máquina normalmente no se cuenta como "código fuente". A diferencia del código fuente, el código de máquina generalmente tiene una alta complejidad de símbolos y, lo que es más importante, no se puede imprimir. Consulte " ¿Deben considerarse los binarios ejecutables una solución razonable para code-golf? ".
  • Esta solución solo toma un solo número como entrada, en lugar de múltiples entradas.
  • Esta solución es una función, no un programa.

Si tiene una TI-89/92 / V200, puede descargar el proyecto completo aquí (desactualizado):

https://rapidshare.com/files/154945328/minfib.zip

Buena suerte persuadiendo a RapidShare para que te dé el archivo real. ¿Alguien sabe de un buen host para archivos tan grandes? 8940 es una gran cantidad de bytes.


Podría agregar un cuarto punto a la lista: la solución no da la salida en el formato correcto: P Estoy usando 7 caracteres solo en literales de cadena. Por cierto, ¿devuelve la lista [0] para la entrada 0? Me parece que devuelve la lista vacía. Es un caso especial irritante.
Peter Taylor

@ Peter Taylor: Tienes razón, me perdí eso. Tengo términos y cuentas de términos mezclados. Publicaré una solución pronto.
Joey Adams

5

Javascript (142)

Solo maneja una sola entrada a la vez. Porque la entrada multilínea es inútil para JavaScript.

k=n=prompt(f=[a=b=1])|0;while((b=a+(a=b))<n)f.push(b);for(i=f.length,g=[];i--;)if(f[i]<=k)g.push(f[i]),k-=f[i];alert(n+': '+(n?g.join('+'):0))

http://jsfiddle.net/EqMXQ/


5

C, 244 caracteres

#define P printf
int f[30];int main(){f[28]=f[29]=1;int i=28;for(;i>0;--i)f[i-1]=f[i+1]+f[i];int x;while(scanf("%i",&x)!=-1){P(x?"%i: ":"0: 0\n",x);if(x>0){int i=0,a=0;while(x>0){while(f[i]>x)++i;if(a++)P("+");P("%i",f[i]);x-=f[i];}P("\n");}}}

Con espacios en blanco:

#define P printf
int f[30];
int main(){
    f[28] = f[29] = 1;
    int i = 28;
    for(; i > 0; --i) f[i-1] = f[i+1] + f[i];
    int x;
    while(scanf("%i",&x) != -1) {
        P(x ? "%i: " : "0: 0\n",x);
        if(x > 0) {
            int i = 0, a = 0;
            while(x > 0) {
                while(f[i] > x) ++i;
                if(a++) P("+");
                P("%i",f[i]);
                x -= f[i];
            }
            P("\n");
        }
    }
}

Este programa leerá los números de la entrada estándar y escribirá en la salida estándar.


5

Golfscript, 43 caracteres

~]{:|': '[{0 1{|>!}{.@+}/;|1$-:|}do]'+'*n}%

Creo que esto probablemente se puede reducir de 3 a 5 caracteres con más esfuerzo. Por ejemplo, el despliegue para luego tirar la matriz se siente desperdiciado.


3

F # - 282252241 caracteres

let mutable d=int(stdin.ReadLine())
let q=d
let rec f x=if x<2 then 1 else f(x-2)+f(x-1)
let s x=
 d<-d-x
 x
printf"%d: %s"q (Core.string.Join("+",[for i in List.filter(fun x->x<d)[for i in 28..-1..0->f i]do if d-i>=0 then yield s i]))

3

Python - 183 caracteres

La mayoría del código maneja múltiples entradas :(

f=lambda a,b,n:b>n and a or f(b,a+b,n)
g=lambda n:n>0and"%d+%s"%(f(0,1,n),g(n-f(0,1,n)))or""
try:
 while 1:
  n=input()
  print "%d: %s"%(n,n<1and"0"or g(n).strip("+"))
except:0

¿Puedes poner el n=input()al final de la línea anterior?
mbomb007

Supongo que sí. : \
st0le

También puedes guardar un personaje eliminando el espacio despuésprint
mbomb007

2

Mathematica 88

n = RandomInteger[10000, 10];

Print[k=#,For[i=99;l={},k>0,If[#<=k,k-=#;l~AppendTo~#]&@Fibonacci@i--];":"l~Row~"+"]&/@n

Ejemplo de salida

3999: 2584+987+377+34+13+3+1
9226: 6765+1597+610+233+21
7225: 6765+377+55+21+5+2
9641: 6765+2584+233+55+3+1
6306: 4181+1597+377+144+5+2
4507: 4181+233+89+3+1
8848: 6765+1597+377+89+13+5+2
6263: 4181+1597+377+89+13+5+1
2034: 1597+377+55+5
6937: 6765+144+21+5+2


1

Scala - 353 caracteres (100 caracteres para manejar múltiples entradas)

def h(m:Int){lazy val f={def g(a:Int,b:Int):Stream[Int]=a #:: g(b,a+b);g(0,1);};if(m==0)println(m+": "+m)else{var s=0;var t= f.takeWhile(_ <= m);var w="";while(s!= m){s+=t.last;w+=t.last+"+";t=t.takeWhile(_<=m-s);};println(m+": "+w.take(w.length-1))}}
Iterator.continually(Console.readLine).takeWhile(_ != "").foreach(line => h(Integer.parseInt(line)))

Iterator.continually(Console.readLine).takeWhile(_ != "").foreach(line => h(Integer.parseInt(line)))podría acortarse para io.Source.stdin.getLines.foreach(l=>h(Integer.parseInt(l)))guardar 40 caracteres ish.
Gareth

1

Python 3 (170 caracteres)

while 1:
 s=input()
 if not s:break
 s=n=int(s);f=[1];t=[]
 while f[-1]<n:f+=[sum(f[-2:])]
 for i in f[::-1]:
  if s>=i:s-=i;t+=[i]
 print(n,'=','+'.join(map(str,t))or 0)

Entrada multilínea, parada en línea vacía


1

C, 151 caracteres

main() {int i=1,n,f[30]={1,1};for(;i++<30;)f[i]=f[i-1]+f[i-2];while(scanf("%d",&n))for(i=30;;--i)if(f[i]<=n){printf("%d\n",f[i]);if(!(n-=f[i]))break;}}

versión legible:

main() {
    int i=1,n,f[30]={1,1};
    for(;i++<30;)f[i]=f[i-1]+f[i-2];
    while(scanf("%d",&n))
        for(i=30;;--i)
            if(f[i]<=n) {
                printf("%d\n",f[i]);
                if (!(n-=f[i])) break;
            }
}

1

R, 170

x=scan();Filter(function(i)cat(unlist(Map(function(d)if(i>=d&&i){i<<-i-d;d},rev(lapply(Reduce(function(f,x)c(f[2],sum(f)),1:94,c(0,1),F,T),head,n=1)))),sep='+',fill=T),x)

Maneja múltiples entradas y el resultado del gato a STDOUT

> x=scan();Filter(function(i)cat(unlist(Map(function(d)if(i>=d&&i){i<<-i-d;d},rev(lapply(Reduce(function(f,x)c(f[2],sum(f)),1:94,c(0,1),F,T),head,n=1)))),sep='+',fill=T),x)
1: 100
2: 200
3: 300
4: 
Read 3 items
89+8+3
144+55+1
233+55+8+3+1
numeric(0)
>

1

R (460 caracteres)

Otra versión que usa R.
Lectura del archivo "input", salida al archivo "output"

d=as.list(as.integer(scan("input","",sep="\n")));n=36;f=rep(1,n);for(i in 3:n){f[i]=f[i-2]+f[i-1]};d2=lapply(d,function(x){a=vector("integer");i=1;while(x>0){id=which(f>=x)[1];if(x==f[id]){x=x-f[id];a[i]=f[id]}else{x=x-f[id-1];a[i]=f[id-1]}i=i+1}a});d=mapply(c,d,d2,SIMPLIFY=0);for(i in 1:length(d)){t=d[[i]];l=length(t);if(l==1){d[[i]]=paste(t[1],t[1],sep=": ")}else{d[[i]]=paste(t[1],": ",paste(t[2:l],collapse="+"),sep="")}}lapply(d,write,"output",append=1)

ejemplo de "entrada"

0
47
3788
1646
25347
677
343
3434

ejemplo de "salida"

0: 0
47: 34+13
3788: 2584+987+144+55+13+5
1646: 1597+34+13+2
25347: 17711+6765+610+233+21+5+2
677: 610+55+8+3+1
343: 233+89+21
3434: 2584+610+233+5+2

Versión más legible:

dt <- as.list(as.integer(scan(file = "input", what = "", sep = "\n")))
n <- 36
fib <- rep(1, n)
for(i in 3:n){fib[i] <- fib[i-2] + fib[i-1]}
dt2 <- lapply(dt, function(x){answ <- vector(mode = "integer")
                               i <- 1
                               while(x > 0){
                                   idx <- which(fib>=x)[1]
                                   if(x == fib[idx]){
                                       x <- x - fib[idx]
                                       answ[i] <- fib[idx]
                                   } 
                                   else {
                                       x <- x - fib[idx-1]
                                       answ[i] <- fib[idx-1]
                                   }
                                   i <- i + 1
                               }
                               answ})
dt <- mapply(FUN = c, dt, dt2, SIMPLIFY = FALSE)
for(i in 1:length(dt)){
    t1 <- dt[[i]]
    t1.len <- length(t1)
    if(t1.len == 1){
        dt[[i]] <- paste(t1[1], t1[1], sep=": ")
    } else {
        dt[[i]] <- paste(t1[1], ": ", paste(t1[2:t1.len], collapse = "+"), sep="")
    }
}
lapply(dt, write, "output", append=TRUE)

0

D (196 caracteres)

Corre con rdmd --eval=…. Esto oculta convenientemente la repetitiva de import x, y, z;y void main() {…}:

int f(int i){return i-->1?f(i--)+f(i):i+2;}int n;foreach(x;std.stdio.stdin.byLine.map!(to!int))writeln(x,": ",x?n=x,reduce!((r,i)=>f(i)<=n?n-=f(i),r~="+"~f(i).text:r)("",29.iota.retro)[1..$]:"0")

0

Usando Java

package org.mindcraft;

import java.util.Scanner;

public class Fibbo {
    public static void main(String[] args) {
    String number = null;
    int tmp, sum;
    int i = 1, j = 1;
    Scanner in = new Scanner(System.in);
    number = in.nextLine();
    String[] arr = number.split(" ");
    for (int it = 0; it < arr.length; it++) {
        tmp = Integer.parseInt(arr[it]);
        String value = tmp+" : ";
        while (tmp > 0) {
            i = 1;
            j = 1;
            for (int k = 0; k < 10000; k++) {
                sum = i + j;
                if (sum > tmp) {
                    //if (value == null) {
                    char ch=value.charAt(value.length()-2);
                    if(ch==':')
                    {
                        value = value+" "+ j + "";
                    } else {
                        value = value + " + " + j;
                    }

                    tmp = tmp - j;
                    break;
                }
                i = j;
                j = sum;
            }
        }
        System.out.println(value);
    }
}
}

Este es el código de golf, así que asegúrese de marcar su respuesta.
KSFT

1
Bienvenido a PPCG! Como dijo KSFT, este es un desafío de código de golf . Por favor, muestre algo de esfuerzo para responder esta pregunta en el menor número de bytes posible. Como mínimo, puede eliminar espacios en blanco innecesarios y usar nombres de clase / método / variable de una letra. Después de hacer esto, incluya también el conteo de bytes en su respuesta.
Martin Ender
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.