Código Golf: Fractran
-
20-09-2019 - |
Pregunta
The Challenge
Escribir un programa que actúa como un Fractran intérprete. El intérprete más corta por el recuento de caracteres, en cualquier idioma, es el ganador. Su programa debe tener dos entradas: El programa fractran a ser ejecutado, y el entero de entrada n. El programa puede estar en cualquier forma que sea conveniente para su programa - por ejemplo, una lista de 2-tuplas, o una lista plana. La salida debe ser un solo número entero, siendo el valor del registro al final de la ejecución.
Fractran
Fractran es un lenguaje esotérico trivial inventado por John Conway . Un programa fractran consiste en una lista de fracciones positivas y un estado n inicial. El intérprete mantiene un contador de programa, en un principio que apunta a la primera fracción en la lista. Fractran programas se ejecutan de la siguiente manera:
- comprobar si el producto del estado actual y la fracción actualmente en el contador de programa es un número entero. Si lo es, se multiplica el estado actual de la fracción actual y restablecer el contador de programa al principio de la lista.
- Haga avanzar el contador de programa. Si se alcanza el final de la lista, alto, de otro modo volver al paso 1.
Para obtener más detalles sobre cómo y por qué funciona Fractran, consulte la entrada esolang y esta entrada en matemáticas buena / mala matemáticas.
Prueba vectores
Programa: [(3, 2)]
Entrada: 72 (2 3 3 2 )
Salida: 243 (3 5 )
Programa: [(3, 2)]
Entrada: 1296 (2 4 3 4 )
Salida: 6561 (3 8 )
Programa: [(455, 33), (11, 13), (1, 11), (3, 7), (11, 2), (1, 3)] < br>
Entrada: 72 (2 3 3 2 )
Salida: 15625 (5 6 )
Bonus prueba vector:
Su presentación no tiene que ejecutar este último programa correctamente para ser una respuesta aceptable. Pero felicitaciones si lo hace!
Programa: [(455, 33), (11, 13), (1, 11), (3, 7), (11, 2), (1, 3)] < br>
Entrada: 60466176 (2 10 3 10 )
Salida: 7888609052210118054117285652827862296732064351090230047702789306640625 (5 100 )
Envío & Scoring
Los programas se clasifican estrictamente por la longitud en caracteres - más corto es el mejor. No dude en enviar un tanto una versión 'minified' de su código bien diseñado y documentado, y que la gente pueda ver lo que está pasando.
El lenguaje 'J' no es admisible. Esto es porque ya hay una solución muy conocida en J en una de las páginas enlazadas. Si eres un fan de J, lo siento!
Como un bono extra, sin embargo, cualquier persona que pueda proporcionar un intérprete fractran de trabajo en fractran recibirán una bonificación del 500 reputación. En el improbable caso de múltiples intérpretes autoalojamiento, el que tiene el menor número de fracciones recibirá la recompensa.
Los ganadores
El ganador oficial, después de presentar una solución fractran autoalojamiento que comprende fracciones 1779, es Jesse Beder de solución. En términos prácticos, la solución es demasiado lento para ejecutar incluso 1 + 1, sin embargo.
Aunque parezca increíble, esto ha sido desde entonces golpeado por otra solución fractran - solución de Amadaeus en sólo 84 fracciones! Es capaz de la ejecución de la primera two casos de prueba en cuestión de segundos cuando se ejecuta en mi solución Python referencia. Se utiliza un nuevo método de codificación para las fracciones, que es también digno de una mirada cercana.
Menciones de honor a:
- de Canon Stephen solución , en 165 caracteres de montaje (28 x 86 bytes de la máquina código)
- solución de Jordan en 52 caracteres de rubíes - el que pasan enteros largos
- solución href="https://stackoverflow.com/questions/1749905/code-golf-fractran/1750633#1750633"> inútil en 87 caracteres de Python, que, aunque no es el más corto de Python solución, es una de las pocas soluciones que no es recursivo, y por lo tanto se ocupa de los programas más difíciles con facilidad. También es muy fácil de leer.
Solución
Fractran - 1779 fracciones
(Edit: fija)
(espero que la gente todavía están siguiendo este hilo, porque esto tomó un tiempo!)
Parece que sí no me deja publicar algo tan larga como esta, por lo que ha escrito la fuente Fractran aquí .
entrada se especifica de la siguiente manera:
En primer lugar, codificamos un m/n = p_0^a0... p_k^ak
fracción por:
- Comience con 1. A continuación, para cada
ai
: - Multiplicar por
p_2i^ai
siai > 0
- Multiplicar por
p_2i+1^{-ai}
sia_i < 0
De esta manera, codificar cualquier fracción como un número entero positivo. Ahora, dado un progoram (secuencia de fracciones codificados F0, F1, ...), codificamos que por
p_0^F0 p1^F1 ...
Finalmente, la entrada al intérprete está dado por:
2^(program) 3^(input) 5
donde program
y input
se codifican como anteriormente. Por ejemplo, en el primer problema prueba, 3/2
se codifica a 15
, por lo que el programa se codifica a 2^15
; y 108
se codifica a 500
. Así, pasamos
2^{2^15} 3^500 5
para el programa. La salida, a continuación, es de la forma
2^(program) 3^(output)
por lo que en el primer ejemplo, va a ser
2^{2^15} 3^3125
¿Cómo funciona?
escribí un metalenguaje que compila a Fractran. Permite funciones (Fractran simple y secuencias de otras funciones), y un bucle while
y cuenta de if
(por conveniencia). El código para que se puede encontrar aquí .
Si quieres compilar el código abajo a Fractran mismo, mi programa (C ++) se puede encontrar aquí [tar.gz.]. En un impresionante despliegue de dogfooding (y mostrando), he usado mi C ++ YAML analizador href="http://code.google.com/p/yaml-cpp/" yaml-CPP , por lo que lo tienes que descargar y enlace con eso. Por tanto yaml-CPP y el "compilador", tendrá que CMake para generar multiplataforma makefile.
El uso de este programa es:
./fracc interpreter.frp
El lee el nombre de una función de la entrada estándar, y escribe el correspondiente "pseudo-Fracción" (I a explicar que en un segundo) a la salida estándar. Así que para compilar el intérprete (Interpretar la función), puede ejecutar
echo "Interpret" | ./fracc interpreter.frp > interpret
La salida ( "pseudo-Fractran") será una secuencia de líneas, cada una con una cadena de dígitos separados por un espacio. Una línea corresponde a una fracción:. Si el dígito n
th en la línea es an
, a continuación, la fracción es el producto de p_n^an
Es muy fácil de convertir esto en Fractran, pero si usted es perezoso, puede utilizar to-fractions.py . [ Nota: : antes yo tenía un programa en C ++, y yo no había hecho caso de desbordamiento de entero sin cuidado. Lo traduje a Python para evitar esto.]
Nota acerca de la entrada : si quieres poner a prueba una función diferente de esta manera, la convención es siempre la misma. Tiene una serie de parámetros (por lo general el comentario anterior la función explica esto) en pseudo-Fractran, por lo que darle lo que quiere, además de un 1
en la siguiente ranura (lo que en Fractran ordinaria, se multiplica una vez por el primer primer que no va a usar). Esto es un poco de "señal" a la función para iniciar la marcha.
Sin embargo,
No recomiendo realidad tratando de ejecutar el intérprete de Fractran (por desgracia). Probé muchos de sus componentes, y, por ejemplo, la IncrementPrimes
función, que tiene un par de números primos y devuelve los próximos dos números primos, toma alrededor de 8 minutos para ejecutar, usando mi tonta C ++ intérprete (sin necesidad de publicar eso :). Además, no hace falta (al menos) al cuadrado del número de llamadas de función - duplicando el número de llamadas de función hace que se tarda por lo menos cuatro veces más largo (más si hay bucles while
o declaraciones if
). Así que supongo que ejecuta el intérprete tendrá al menos días, si no años: (
Entonces, ¿cómo sé que funciona? Bueno, por supuesto que no estoy 100% seguro, pero estoy bastante cerca. Primero de todo, he probado muchos, muchos de sus componentes, y, en particular, he probado todos los elementos de la meta-lenguaje (secuencias de funciones y estados if
y while
) muy a fondo.
Además, el metalenguaje es fácil de traducir a su idioma favorito, e incluso más fácil de traducir a C ++, ya que todos los parámetros de las funciones se pasan por referencia. Si estás perezoso de nuevo, se puede descargar mi traducción href="http://www.math.uiuc.edu/~beder/interpreter-cpp.tar.gz" aquí [tar.gz.] (no hay makefile, es sólo dos archivos .cpp, así que llamar directamente gcc está muy bien)
.Así se puede comparar los dos intérpretes, ejecutar la versión de C ++ (que también toma la entrada / salida de pseudo-Fractran), compruebe que que funciona, y luego convencerse de que el meta-lenguaje también funciona.
O!
Si te sientes inspirado, y realmente quieren ver este intérprete interpreta, puede escribir un intérprete de "inteligente" Fractran en torno al tipo de salida Fractran que obtenemos. La salida es muy estructurado - sucesiones de funciones se implementan utilizando señales, por lo que si de alguna manera caché donde estaba el intérprete, que podrían saltar allí inmediatamente si nada importante ha cambiado. Esto, creo, sería acelerar dramáticamente el programa (tal vez reducir el tiempo de funcionamiento de una o más potencias).
Sin embargo, no estoy muy seguro de cómo hacer esto, y estoy feliz con lo que está hecho, así que lo dejo como ejercicio para el lector.
Otros consejos
Fractran: 84 fracciones
FTEVAL = [197*103/(2^11*101), 101/103, 103*127/(2*101), 101/103, 109/101,
2*23/(197*109), 109/23, 29/109,197*41*47/(31*59), 11^10*53/(127*197), 197/53,
37/197, 7^10*43/(11^10*37), 37/43, 59/(37*47), 59/47, 41*61/59, 31*67/(41*61),
61/67, 7*67/(127*61), 61/67,101/71, 73/(127^9*29), 79/(127^2*73),
83/(127*73), 89/(2*29), 163/29, 127^11*89/79, 337/83, 2*59/89, 71/61,
7*173/(127*163), 163/173, 337*167/163, 347/(31*337), 337/347, 151/337,
1/71,19*179/(3*7*193), 193/179, 157/(7*193), 17*181/193, 7*211/(19*181),
181/211, 193/181, 157/193, 223/(7*157), 157/223, 281*283/239,
3*257*269/(7*241), 241/269, 263/241, 7*271/(257*263), 263/271, 281/263,
241/(17*281), 1/281, 307/(7*283), 283/307, 293/283, 71*131/107, 193/(131*151),
227/(19*157), 71*311/227, 233/(151*167*311), 151*311/229, 7*317/(19*229),
229/317, 239*331/217, 71*313/157, 239*251/(151*167*313), 239*251/(151*313),
149/(251*293), 107/(293*331), 137/199, 2^100*13^100*353/(5^100*137),
2*13*353/(5*137), 137/353, 349/137, 107/349, 5^100*359/(13^100*149),
5*359/(13*149), 149/359, 199/149]
Esto está escrito totalmente a mano. Me hice un pseudo lenguaje para poder expresar las cosas con más claridad, pero no me hizo escribir un compilador y optó por escribir código optimizado Fractran directamente.
FTEVAL toma 3^initial_state * 5^encoded_program * 199
de entrada, produce resultados intermedio 3^interpreted_program_state * 199
, y completamente se detiene en un número divisible por 233
.
El programa interpretado se Embebido como una lista de base de 10 dígitos dentro de un único número de base 11, usando el dígito "a" para marcar el límite excepto en el final. El programa de adición [3/2] se codifica como
int("3a2", 11) = 475.
El programa de multiplicación [455/33, 11/13, 1/11, 3/7, 11/2, 1/3] se codifica como
int("1a3a11a2a3a7a1a11a11a13a455a33", 11) = 3079784207925154324249736405657
que es un número realmente grande.
El primer vector de prueba terminado en menos de un segundo , produjo el resultado deseado después de 4545 iteraciones y se detuvo después de 6172 iteraciones. Aquí es la salida completa .
Por desgracia, salvia segfaulted cuando probé el segundo vector de prueba (pero creo que va a trabajar en fase de ejecución de Nick usando vectores exponente).
El espacio aquí es realmente demasiado pequeño para explicar todo. Pero aquí está mi pseudocódigo. Voy a escribir mi proceso en un par de días, es de esperar.
# Notations:
# %p
# designates the exponent of prime factor p that divides the
# current state.
# mov x y
# directly translates to the fraction y/x; its meaning: test if x divides
# the current state, if so divide the current state by x and multiply it by
# y. In effect, the prime exponents of x and y are exchanged. A Fractran
# program only comprises of these instructions; when one is executable, the
# program continues from the beginning.
# dec x => mov x, 1
# wipes out all factors of x
# inc x => mov 1, x
# this form is here for the sake of clarity, it usually appears in a
# loop's entry statement and is merged as such when compiled
# sub n ret m {...}
# conceptually represents a Fractran sub-program, which will execute just
# like a normal Fractran program, that is, the sub-program's statements
# execute when triggered and loop back. The sub-program only exits when none of
# its statement is executable, in which occasion we multiply the program's
# state by m. We can also explicitly break out early on some conditions.
# It is also possible to enter a sub-prorgram via multiple entry points and
# we must take care to avoiding this kind of behavior (except in case where
# it is desirable).
# entry point 101: return 29
# Divide %2 modulo 11:
# - quotient is modified in-place
# - remainder goes to %127
sub 101 ret 101 { mov 2^11, 197 }
sub 101 ret 109 { mov 2, 127 }
sub 109 ret 29 { mov 197, 2 }
# entry point 59: return 61
# Multiply %127 by 10^%31 then add result to %7,
# also multiply %31 by 10 in-place.
sub 59 ret 41*61 {
mov 31, 197*41
sub 197 ret 37 { mov 127, 11^10 }
sub 37 { mov 11^10, 7^10 }
}
sub 61 ret 61 { mov 41, 31 }
sub 61 ret 61 { mov 127, 7 } # the case where %31==0
# entry point 71: return 151 if success, 151*167 if reached last value
# Pop the interpreted program stack (at %2) to %7.
sub 71 {
# call sub 101
inc 101
# if remainder >= 9:
mov 29*127^9, 73
# if remainder == 11, goto 79
mov 73*127^2, 79
# else:
# if remainder == 10, goto 83
mov 73*127, 83
# else:
# if quotient >= 1: goto 89
mov 29*2, 89
# else: goto 163
mov 29, 163
# 79: restore remainder to original value, then goto 89
mov 79, 127^11*89
# 83: reached a border marker, ret
mov 83, 337
# 89: the default loop branch
# restore quotient to original value, call 59 and loop when that rets
mov 2*89, 59
mov 61, 71
# 163: reached stack bottom,
# ret with the halt signal
sub 163 ret 337*167 { mov 127, 7 }
# 337: clean up %31 before ret
sub 337 ret 151 { dec 31 }
}
# entry point 193, return 157
# Divide %3 modulo %7:
# - quotient goes to %17
# - remainder goes to %19
sub 193 ret 17*181 {
mov 3*7, 19
}
mov 7*193, 157
sub 181 ret 193 { mov 19, 7 }
mov 193, 157
sub 157 ret 157 { dec 7 }
# entry point 239: return 293
# Multiply %17 by %7, result goes to %3
mov 239, 281*283
sub 241 { mov 7, 3*257 }
sub 263 ret 281 { mov 257, 7 }
mov 281*17, 241
sub 283 ret 293 { dec 7 }
# entry point 107: return 149 if success, 233 if stack empty
# Pop the stack to try execute each fraction
sub 107 {
# pop the stack
inc 71*131
# 151: popped a value
# call divmod %3 %7
mov 131*151, 193
# if remainder > 0:
mov 19*157, 227
# pop and throw away the numerator
mov 227, 71*311
# if stack is empty: halt!
mov 151*167*311, 233
# else: call 239 to multiply back the program state and gave loop signal
mov 151*311, 229
sub 229 ret 239*331 { mov 19, 7 }
# else: (remainder == 0)
# pop the numerator
mov 157, 71*313
# clear the stack empty signal if present
# call 239 to update program state and gave ret signal
mov 151*167*313, 239*251
mov 151*313, 239*251
# after program state is updated
# 313: ret
mov 293*251, 149
# 331: loop
mov 293*331, 107
}
# main
sub 199 {
# copy the stack from %5 to %2 and %13
sub 137 ret 137 { mov 5^100, 2^100*13^100 }
sub 137 ret 349 { mov 5, 2*13 }
# call sub 107
mov 349, 107
# if a statement executed, restore stack and loop
sub 149 ret 149 { mov 13^100, 5^100 }
sub 149 ret 199 { mov 13, 5 }
}
conjunto x86_64 , 165 caracteres (28 bytes de código de máquina).
Estado se pasa en% RDI, Programa (puntero a la matriz terminada en nulo de fracciones) es en% RSI. Los resultados se devuelven en% rax por las convenciones de llamada de tipo C habituales. El uso de convenciones de llamada no estándar o sintaxis de Intel (esto es AT & T sintaxis) se reduciría un poco más personajes, pero soy perezoso; otra persona puede hacerlo. Una instrucción o dos casi con toda seguridad se puede guardar reorganizando el flujo de control, si alguien quiere hacer eso, se siente libre.
cálculos intermedios (estado * numerador) puede ser de hasta 128 bits de ancho, pero está apoyado sólo el 64 estado de bit.
_fractran:
0: mov %rsi, %rcx // set aside pointer to beginning of list
1: mov (%rcx), %rax // load numerator
test %rax, %rax // check for null-termination of array
jz 9f // if zero, exit
mul %rdi
mov 8(%rcx), %r8 // load denominator
div %r8
test %rdx, %rdx // check remainder of division
cmovz %rax, %rdi // if zero, keep result
jz 0b // and jump back to program start
add $16, %rcx // otherwise, advance to next instruction
jmp 1b
9: mov %rdi, %rax // copy result for return
ret
Eliminar comentarios, espacios en blanco extrañas, y el _fractran
etiqueta detallado para la versión minimizada.
Ruby, 58 57 56 53 52 caracteres
Esta es mi primera vez la introducción del código de golf, así que por favor ser suave.
def f(n,c)d,e=c.find{|i,j|n%j<1};d ?f(n*d/e,c):n end
Uso:
irb> f 108, [[455, 33], [11, 13], [1,11], [3,7], [11,2], [1,3]]
=> 15625
irb> f 60466176, [[455, 33], [11, 13], [1, 11], [3, 7], [11, 2], [1, 3]]
=> 7888609052210118054117285652827862296732064351090230047702789306640625
versión de Pretty (252):
def fractran(instruction, program)
numerator, denominator = program.find do |numerator, denominator|
instruction % denominator < 1
end
if numerator
fractran(instruction * numerator / denominator, program)
else
instruction
end
end
Ruby, 53 52 usando racionales
yo era capaz de conseguir una solución a través de Rational hasta 53 52 caracteres. Sigue siendo uno más largo que la solución (menos elegante) anterior.
def f(n,c)c.map{|i|return f(n*i,c)if i*n%1==0};n end
Uso:
irb> require 'rational'
irb> f 60466176, [Rational(455, 33), Rational(11, 13), Rational(1, 11), Rational(3, 7), Rational(11, 2), Rational(1, 3)]
=> Rational(7888609052210118054117285652827862296732064351090230047702789306640625, 1)
(Una llamada to_i
para la salida más bonita añadiría más de 5 caracteres.)
Golfscript - 32
{:^{1=1$\%!}?.1={~@\/*^f}{}if}:f ; 108 [[3 2]] f p # 243 ; 1296 [[3 2]] f p # 6561 ; 108 [[455 33][11 13][1 11][3 7][11 2][1 3]] f p # 15625 ; 60466176 [[455 33][11 13][1 11][3 7][11 2][1 3]] f p # 7888609052210118054117285652827862296732064351090230047702789306640625
Haskell , 102 caracteres
import List
import Ratio
l&n=maybe n((&)l.numerator.(n%1*).(!!)l)$findIndex((==)1.denominator.(n%1*))l
$ ghci Prelude> :m List Ratio Prelude List Ratio> let l&n=maybe n((&)l.numerator.(n%1*).(!!)l)$findIndex((==)1.denominator.(n%1*))l Prelude List Ratio> [3%2]&108 243 Prelude List Ratio> [3%2]&1296 6561 Prelude List Ratio> [455%33,11%13,1%11,3%7,11%2,1%3]&108 15625
88 con suavizó las restricciones sobre el formato de entrada / salida.
import List
import Ratio
l&n=maybe n((&)l.(*)n.(!!)l)$findIndex((==)1.denominator.(*)n)l
Prelude List Ratio> let l&n=maybe n((&)l.(*)n.(!!)l)$findIndex((==)1.denominator Prelude List Ratio> [455%33,11%13,1%11,3%7,11%2,1%3]&108 15625 % 1
Python, 83 82 81 72 70 caracteres.
Es conveniente tener de entrada como objetos fractions.Fraction. La misma idea que en la solución de Ruby.
def f(n,c):d=[x for x in c if x*n%1==0];return d and f(n*d[0],c) or n
# Test code:
from fractions import Fraction as fr
assert f(108, [fr(3, 2)]) == 243
assert f(1296, [fr(3, 2)]) == 6561
assert f(108, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 15625
assert f(60466176, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 7888609052210118054117285652827862296732064351090230047702789306640625
C , 159 153 151 131 111 110 caracteres
v[99],c,d;main(){for(;scanf("%d",v+c++););while(d++,v[++d])
*v%v[d]?0:(*v=*v/v[d]*v[d-1],d=0);printf("%d",*v);}
$ cc f.c $ echo 108 3 2 . | ./a.out; echo 243 $ echo 1296 3 2 . | ./a.out; echo 6561 $ echo 108 455 33 11 13 1 11 3 7 11 2 1 3 . | ./a.out; echo 15625
Python - 53
Mejora gracias a Paul
f=lambda n,c:next((f(n*x,c)for x in c if x*n%1==0),n)
casos de prueba
from fractions import Fraction as fr
assert f(108, [fr(3, 2)]) == 243
assert f(1296, [fr(3, 2)]) == 6561
assert f(108, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 15625
assert f(60466176, [fr(455, 33), fr(11, 13), fr(1, 11), fr(3, 7), fr(11, 2), fr(1, 3)]) == 7888609052210118054117285652827862296732064351090230047702789306640625
Python - 54 Sin utilizar Fracción
f=lambda n,c:next((f(n*i/j,c)for i,j in c if n%j<1),n)
Python - 55
Éste es algo teórico. Los dos primeros casos funcionan bien, pero los otros dos fallan a nivel de recursividad. Tal vez alguien puede conseguir que funcione con una expresión generadora
f=lambda n,c:([f(n*i/j,c)for i,j in c if n%j<1]+[n])[0]
Esto es una posibilidad, pero crece a 65, incluso sin incluir la importación
from itertools import chain
f=lambda n,c:(chain((f(n*i/j,c)for i,j in c if n%j<1),[n])).next()
F #: 80 caracteres
let rec f p=function|x,(e,d)::t->f p (if e*x%d=0I then(e*x/d,p)else(x,t))|x,_->x
Esto es una versión ampliada utilizando match pattern with |cases
en lugar de function
:
//program' is the current remainder of the program
//program is the full program
let rec run program (input,remainingProgram) =
match input, remainingProgram with
| x, (e,d)::rest ->
if e*x%d = 0I then //suffix I --> bigint
run program (e*x/d, program) //reset the program counter
else
run program (x, rest) //advance the program
| x, _ -> x //no more program left -> output the state
Código de ensayo:
let runtests() =
[ f p1 (108I,p1) = 243I
f p1 (1296I,p1) = 6561I
f p2 (108I,p2) = 15625I
f p2 (60466176I,p2) = pown 5I 100]
Y resultado (probado en Fa # interactiva):
> runtests();;
val it : bool list = [true; true; true; true]
Editar vamos a tener un poco más de diversión con este, y calcular algunos números primos (véase la página vinculada en el puesto de partida). He escrito un nuevo g
función que se obtienen los valores intermedios del estado.
//calculate the first n primes with fractran
let primes n =
let ispow2 n =
let rec aux p = function
| n when n = 1I -> Some p
| n when n%2I = 0I -> aux (p+1) (n/2I)
| _ -> None
aux 0 n
let pp = [(17I,91I);(78I,85I);(19I,51I);(23I,38I);(29I,33I);(77I,29I);(95I,23I);
(77I,19I);(1I,17I);(11I,13I);(13I,11I);(15I,14I);(15I,2I);(55I,1I)]
let rec g p (x,pp) =
seq { match x,pp with
|x,(e,d)::t -> yield x
yield! g p (if e*x%d=0I then (e*x/d,p) else (x,t))
|x,_ -> yield x }
g pp (2I,pp)
|> Seq.choose ispow2
|> Seq.distinct
|> Seq.skip 1 //1 is not prime
|> Seq.take n
|> Seq.to_list
Toma la friolera de 4,7 segundos que soltar los primeros 10 números primos:
> primes 10;;
Real: 00:00:04.741, CPU: 00:00:04.005, GC gen0: 334, gen1: 0, gen2: 0
val it : int list = [2; 3; 5; 7; 11; 13; 17; 19; 23; 29]
Este es, sin duda, el generador de número primo más extraño y lento que he escrito. No estoy seguro de si eso es algo bueno o malo.
A Javascript uno: 99 caracteres . Sin vector de bonificación: (
function g(n,p,q,i,c){i=0;while(q=p[i],c=n*q[0],(c%q[1]?++i:(n=c/q[1],i=0))<p.length){};return n;};
entrada está en el formato [[a,b],[c,d]]
. Aproveché indulgencia de Javascript: en lugar de hacer var x=0, y=0;
, puede añadir tantos parámetros como desee. No importa si en realidad se pasa o no, ya que por defecto a null
.
Pretty versión:
function g(n,p) { var q, c, i=0; while(i < p.length) { q = p[i]; c = n * q[0]; if(c % q[1] != 0) { ++i; } else { n = c % q[1]; i = 0; } } return n; };
Python, 110 103 95 87 caracteres
frc.py
def f(n,c):
d=c
while len(d):
if n%d[1]:d=d[2:]
else:n=d[0]*n/d[1];d=c
return n
test.py
(muestra cómo manejarlo)
from frc import f
def test():
"""
>>> f(108, [3,2])
243
>>> f(1296, [3,2])
6561
>>> f(108, [455,33,11,13,1,11,3,7,11,2,1,3])
15625
>>> f(60466176, [455, 33,11, 13,1, 11,3, 7,11, 2,1, 3])
7888609052210118054117285652827862296732064351090230047702789306640625L
"""
pass
import doctest
doctest.testmod()
C #:
versión ordenado:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Test
{
class Program
{
static void Main(string[] args)
{
int ip = 1;
decimal reg = Convert.ToInt32(args[0]);
while (true)
{
if (ip+1 > args.Length)
{
break;
}
decimal curfrac = Convert.ToDecimal(args[ip]) / Convert.ToDecimal(args[ip+1]);
if ((curfrac * reg) % 1 == 0)
{
ip = 1;
reg = curfrac * reg;
}
else
{
ip += 2;
}
}
Console.WriteLine(reg);
Console.ReadKey(true);
}
}
}
versión reducida con un peso de 201 caracteres (sin las declaraciones de espacio de nombres ni nada de eso, sólo la única instrucción using (no del sistema) y la función principal):
using System;namespace T{using b=Convert;static class P{static void Main(string[] a){int i=1;var c=b.ToDecimal(a[0]);while(i+1<=a.Length){var f=b.ToDecimal(a[i])/b.ToDecimal(a[i+1]);if((f*c)%1==0){i=1;c*=f;}else{i+=2;}}Console.Write(c);}}}
Ejemplos (de entrada es a través de los argumentos de línea de comandos):
input: 108 3 2
output: 243.00
input: 1296 3 2
output: 6561.0000
input: 108 455 33 11 13 1 11 3 7 11 2 1 3
output: 45045.000000000000000000000000
Groovy , 136 117 107 caracteres.
Llamada [estado de entrada] fractal.groovy como maravilloso [vector programa como lista de números]
a=args.collect{it as int}
int c=a[0]
for(i=1;i<a.size;i+=2) if(c%a[i+1]==0){c=c/a[i+1]*a[i];i=-1}
println c
Ejemplo
bash$ groovy fractal.groovy 108 455 33 11 13 1 11 3 7 11 2 1 3
Output: 15625
Perl, 84 82 Char
Utiliza la entrada estándar.
@P=<>=~/\d+/g;$_=<>;
($a,$%)=@P[$i++,$i++],$_*$a%$%or$i=0,$_*=$a/$%while$i<@P;
print
Toma 110 caracteres para pasar la prueba de bonificación:
use Math'BigInt blcm;@P=<>=~/\d+/g;$_=blcm<>;
($%,$=)=@P[$i++,$i++],$_*$%%$=or$i=0,($_*=$%)/=$=while$i<@P;print
Haskell: 116 109 caracteres
f p x[]=x
f p x((n,d):b)|x*n`mod`d==0=f p(x*n`div`d)p|True=f p x b
main=do{p<-readLn;x<-readLn;print$f p x p}
Esto terminó como algo de una imitación de la entrada de Darío.
Esquema: 326
pensé que era necesaria una presentación esquema, para la paridad. Asimismo, sólo quería una excusa para jugar con él. (Disculpen mi conocimiento rudimentario, estoy seguro de que esto podría ser optimizado, y estoy abierto a sugerencias!)
#lang scheme
(define fractran_interpreter
(lambda (state pc program)
(cond
((eq? pc (length program))
(print state))
((integer? (* state (list-ref program pc)))
(fractran_interpreter (* state (list-ref program pc)) 0 program))
(else
(fractran_interpreter state (+ pc 1) program)))))
Pruebas:
(fractran_interpreter 108 0 '(3/2))
(fractran_interpreter 60466176 0 '(455/33 11/13 1/11 3/7 11/2 1/3))
Me sale el vector de bonificación! (usando Dr. Scheme, la asignación de 256 mb)
Lua:
código ordenado:
a=arg;
ip=2;
reg=a[1];
while a[ip] do
curfrac = a[ip] / a[ip+1];
if (curfrac * reg) % 1 == 0 then
ip=2;
reg = curfrac * reg
else
ip=ip+2
end
end
print(reg)
código compacto con un peso de 98 caracteres (reducción sugerida por Scoregraphic en mi otra respuesta, y más sugerido por gwell):
a=arg i=2 r=a[1]while a[i]do c=a[i]/a[i+1]v=c*r if v%1==0 then i=2 r=v else i=i+2 end end print(r)
Ejecutar desde la línea de comandos, suministrando el número base primera entonces la serie de fracciones que se presentan como números con separación espacial, como la siguiente:
C:\Users\--------\Desktop>fractran.lua 108 3 2
243
C:\Users\--------\Desktop>fractran.lua 1296 3 2
6561
C:\Users\--------\Desktop>fractran.lua 108 455 33 11 13 1 11 3 7 11 2 1 3
15625
(tecleado manualmente algo de eso en porque es un dolor para conseguir cosas fuera de la línea de comandos, aunque eso es los resultados devueltos)
No maneja el vector de bonificación por desgracia: (
implementación de referencia en Python
Esta aplicación funciona en factores primos.
En primer lugar, decodifica una lista de tuplas fracción codificando el numerador y el denominador como una lista de (IDX valor) tuplas, donde idx es el número del primer (2 es primo 0, 3 es primo 1, y así sucesivamente).
El estado actual es una lista de exponentes para cada primo, por el índice. Ejecución de una instrucción requiere primera iteración sobre el denominador, comprobando si el elemento de estado indexada es al menos el valor especificado, entonces, si coincide, decrementar elementos estatales especificados en el denominador, e incrementando los especificados en el numerador.
Este enfoque es de aproximadamente 5 veces la velocidad de hacer operaciones aritméticas con números enteros grandes en Python, y es mucho más fácil de depurar!
Una optimización adicional se proporciona mediante la construcción de una matriz mapear cada índice prime (variable) a la primera vez que se comprueba en el denominador de una fracción, a continuación, utilizando que para construir un 'jump_map', que consta de la siguiente instrucción a ejecutar para cada instrucción en el programa.
def primes():
"""Generates an infinite sequence of primes using the Sieve of Erathsones."""
D = {}
q = 2
idx = 0
while True:
if q not in D:
yield idx, q
idx += 1
D[q * q] = [q]
else:
for p in D[q]:
D.setdefault(p + q, []).append(p)
del D[q]
q += 1
def factorize(num, sign = 1):
"""Factorizes a number, returning a list of (prime index, exponent) tuples."""
ret = []
for idx, p in primes():
count = 0
while num % p == 0:
num //= p
count += 1
if count > 0:
ret.append((idx, count * sign))
if num == 1:
return tuple(ret)
def decode(program):
"""Decodes a program expressed as a list of fractions by factorizing it."""
return [(factorize(n), factorize(d)) for n, d in program]
def check(state, denom):
"""Checks if the program has at least the specified exponents for each prime."""
for p, val in denom:
if state[p] < val:
return False
return True
def update_state(state, num, denom):
"""Checks the program's state and updates it according to an instruction."""
if check(state, denom):
for p, val in denom:
state[p] -= val
for p, val in num:
state[p] += val
return True
else:
return False
def format_state(state):
return dict((i, v) for i, v in enumerate(state) if v != 0)
def make_usage_map(program, maxidx):
firstref = [len(program)] * maxidx
for i, (num, denom) in enumerate(program):
for idx, value in denom:
if firstref[idx] == len(program):
firstref[idx] = i
return firstref
def make_jump_map(program, firstref):
jump_map = []
for i, (num, denom) in enumerate(program):
if num:
jump_map.append(min(min(firstref[idx] for idx, val in num), i))
else:
jump_map.append(i)
return jump_map
def fractran(program, input, debug_when=None):
"""Executes a Fractran program and returns the state at the end."""
maxidx = max(z[0] for instr in program for part in instr for z in part) + 1
state = [0]*maxidx
if isinstance(input, (int, long)):
input = factorize(input)
for prime, val in input:
state[prime] = val
firstref = make_usage_map(program, maxidx)
jump_map = make_jump_map(program, firstref)
pc = 0
length = len(program)
while pc < length:
num, denom = program[pc]
if update_state(state, num, denom):
if num:
pc = jump_map[pc]
if debug_when and debug_when(state):
print format_state(state)
else:
pc += 1
return format_state(state)
Perl 6: 77 caracteres (experimental)
sub f(@p,$n is copy){
loop {my$s=first {!($n%(1/$_))},@p or return $n;$n*=$s}}
Newline es opcional. Llamar como:
say f([3/2], 1296).Int;
say f([455/33, 11/13, 1/11, 3/7, 11/2, 1/3], 60466176).Int;
versión legible:
sub Fractran (@program, $state is copy) {
loop {
if my $instruction = first @program:
-> $inst { $state % (1 / $inst) == 0 } {
$state *= $instruction;
} else {
return $state.Int;
}
}
}
Notas:
-
El
first @program: pointy-sub
notación de colon no funciona en las implementaciones actuales; primero BLOCK, @program tiene que ser utilizado en su lugar. -
Rakudo parece tener un cochecito
Rat
dar resultados incorrectos. Niecza actual ejecuta todos los programas de prueba correctamente y rápidamente, incluyendo la fracción "prima".
Haskell, 142 caracteres
Sin ninguna bibliotecas adicionales y I completo / O.
t n f=case f of{(a,b):f'->if mod n b == 0then(\r->r:(t r f))$a*n`div`b else t n f';_->[]}
main=readLn>>=(\f->readLn>>=(\n->print$last$t n f))
Java, 200 192 179 caracteres
Creo que todo el mundo sabe que Java no tendría la aplicación más corto, pero quería ver cómo se compararía. Resuelve los ejemplos triviales, pero no el bono único.
Esta es la versión minimizada:
class F{public static void main(String[]a){long p=new Long(a[0]);for(int i=1;i<a.length;){long n=p*new Long(a[i++]),d=new Long(a[i++]);if(n%d<1){p=n/d;i=1;}}System.out.print(p);}}
java -cp. F 108 455 33 11 13 1 11 3 7 11 2 1 3
15625
java -cp. F 1,296 3 2
6561
Esta es la versión limpia-up:
public class Fractran {
public static void main(String[] args) {
long product = new Long(args[0]);
for (int index = 1; index < args.length;) {
long numerator = product * new Long(args[index++]);
long denominator = new Long(args[index++]);
if (numerator % denominator < 1) {
product = numerator / denominator;
index = 1;
} // if
} // for
System.out.print(product);
}
}
Esquema 73 caracteres
Mi primer intento, al hacer esto con R completamente estándar 5 RS Esquema, se situó en 104 caracteres:
(define(f p n)(let l((q p)(n n))(if(null? q)n(let((a(* n(car q))))(if(integer? a)(l p a)(l(cdr q)n))))))
Correr contra algunos artículos en el vector de prueba:
> (f '(3/2) 1296) 6561 > (f '(455/33 11/13 1/11 3/7 11/2 1/3) 60466176) 7888609052210118054117285652827862296732064351090230047702789306640625
Si se estima que λ
está obligado a lambda
y se define let/cc
(como lo son en el Esquema PLT; véase más adelante para las definiciones para la ejecución de este en los Esquemas que no definen aquellos), entonces se puede adaptar Jordan's segunda solución Rubí al Esquema, que sale a 73 caracteres (tenga en cuenta que el orden de los argumentos es el reverso de mi primera solución, pero lo mismo que Jordan, en esta versión, que salva un carácter):.
(define(f n p)(let/cc r(map(λ(i)(if(integer?(* n i))(r(f(* n i)p))))p)n))
Si no tengo λ
y let/cc
predefinido, entonces éste está en el puesto 111 caracteres (88 si se define la abreviatura call/cc
bastante común):
(define(f n p)(call-with-current-continuation(lambda(r)(map(lambda(i)(if(integer?(* n i))(r(f(* n i)p))))p)n)))
Definiciones de λ
y let/cc
:
(define-syntax λ (syntax-rules () ((_ . body) (lambda . body))) (define-syntax let/cc (syntax-rules () ((_ var . body) (call-with-current-continuation (lambda (var) . body)))))
Un poco tarde ... dc 84 caracteres
Sólo por diversión una solución dc
(OpenBSD)
[ss1sp]s1[Rlp1+sp]s2?l1xz2/sz[z2/ds_:bl_:az0<L]dsLx
1[;als*lp;b~0=1e2lpdlz!<L]dsLxlsp
Maneja todos los casos:
$ dc fractran.dc
455 33 11 13 1 11 3 7 11 2 1 3 60466176
7888609052210118054117285652827862296732064351090230047702789306640625
No puedo dejar comentarios todavía, pero aquí es una versión "ligeramente" más corto de C # versión de RCIX (creo que es de 7 caracteres más cortos)
using System;namespace T{static class P{static void Main(string[] a){int i=1;Func<string,decimal> d=Convert.ToDecimal;var c=d(a[0]);while(i+1<=a.Length){var f=d(a[i])/d(a[++i]);if((f*c)%1==0){i=1;c*=f;}else i++;}Console.Write(c);}}}
que utiliza
Func<string,decimal> d=Convert.ToDecimal
y llamadas d();
en lugar de
using b=Convert;
y llamar repetidamente b.ToDecimal();
.
También se han quitado un par innecesario de llaves de todo el else para ganar 1 carácter:.)
I también se sustituye el a[i+1]
con a[++i]
y en el siguiente cuerpo más Substituí i+=2
con i++
para ganar otro Char: P