Representación más corta de un número de baja carga

13

Texto de sabor

El esolang basado en pila baja carga tiene algunos vínculos interesantes a la programación funcional. Uno de ellos es su tratamiento del tipo de datos numéricos: como el cálculo lambda, usted representa el número natural N por una función que realiza una acción N veces.

Para simplificar las cosas, solo consideraremos el siguiente subconjunto de comandos de Subcarga:

  • : - Este comando duplica el elemento superior en la pila.
  • * - Este comando concatena los dos primeros elementos de la pila en un solo elemento.

Definimos un número N de subcarga como una cadena de :y *que, cuando se ejecuta, consume el elemento superior de la pila y produce N copias de ese elemento concatenados juntos. Algunos ejemplos:

  • No hay números de subcarga 0, -1, 1/2, π.
  • La cadena vacía es el número 1 de baja carga, porque deja la pila intacta.
  • :*es el número 2 de Subcarga, porque duplica el elemento superior y luego concatena esas dos copias en un solo elemento: (A):*= (A)(A)*= (AA).
  • ::**es el número de subcarga 3: (A)::**= (A)(A):**= (A)(AA)*= (AAA).
  • :::*** es el número de subcarga 4.
  • :*:*es también el número de subcarga 4: (A):*:*= (AA):*= (AA)(AA)*= (AAAA).

En general, encontrará que, si My Nson los números de Subcarga M y N, entonces :N*es el número N + 1, y MNes el número M × N.

El reto

Su tarea es escribir el programa más corto (tomando entrada en STDIN) o la función (tomando entrada a través del argumento) que produce la representación más corta del número de Subcarga para su entrada como una cadena. Es decir, si la entrada es un número natural positivo N> 1, debe producir un número de subcarga N cuya longitud en caracteres es menor o igual que la de cualquier otro número de subcarga N.

Muestra de entradas y salidas: ("Entrada - OUTPUT.")

  • 1 - .
  • 2 - :*.
  • 5 - ::*:**(2 × 2 + 1).
  • 7 - ::*::***(2 × 3 + 1) o :::**:**(3 × 2 + 1).
  • 33 - ::*:*:*:*:**(2 × 2 × 2 × 2 × 2 + 1).
  • 49 - ::*:*:*:*::***(16 × 3 + 1, longitud 14) pero no ::*::***::*::***(7 × 7, longitud 16).

Si la entrada no es un número natural positivo, puede devolver un error, producir un comportamiento indefinido o incluso no terminar. Se agradece una explicación del método de su presentación para encontrar la respuesta.

Se aplican restricciones de laguna estándar: sin entrada adicional, sin solicitudes web, el valor de salida / retorno debe ser exactamente la respuesta y no un flujo aleatorio infinito de :y *, etc.

Algoritmo de tiburón
fuente
@Geobits No he dicho nada sobre el tiempo de ejecución, por lo que, siempre y cuando pueda probar que finalmente dará la respuesta correcta, está bien.
algorithmshark
2
Este problema se relaciona con las cadenas de adición; específicamente, la longitud de la respuesta correcta para la entrada xes 2*A117498(x)donde A117498 proporciona la combinación óptima de métodos binarios y factoriales para encontrar una cadena de suma.
Peter Taylor

Respuestas:

4

GolfScript ( 61 60 55 54 53 caracteres)

~:X'']({:A{.'.+'\*A{2$+}%~}%}*{,}${1\~X=}?{44/'*:'=}%

Esto es menos complicado que mi versión anterior y adopta un enfoque ligeramente diferente, pero sigue siendo la fuerza bruta. Sabemos que ':'X*'*'X*+es una solución candidata, por lo tanto, si generamos todas las cadenas bien equilibradas hasta esa longitud y tomamos la más corta que evalúa lo correcto, podemos estar seguros de encontrar una.

# Evaluate input and store the target number in X
~:X
# Seed the generator with the empty string
'']
# X times...
({
    # Store the array of strings so far into A
    :A
    # Generate A' by mapping each element
    {
        # Dup: this leaves an untouched copy of the current string
        .
        # Wrap the duplicate in .+
        '.+'\*
        # For each element in A, generate that element suffixed with the current string
        A{2$+}%~
    }%
}*
# Order by length
{,}$
# Find the first element which evaluates to X
{1\~X=}?
# tr .+ :*
{44/'*:'=}%

Gracias a Howard, de cuya solución he robado un par de ajustes de 1 char.

Peter Taylor
fuente
Jaja, una entrada de 3 tarda más de tres segundos en ejecutarse en el intérprete web. Golf en su máxima expresión.
algorithmshark
@algorithmshark, puedes acelerarlo un poco con un poco de deduplicación. Insertar .&justo después del bucle interno (es decir, entre ~}%y }*.
Peter Taylor
4

GolfScript ( 54 53 caracteres)

Este es un enfoque que está en el espíritu de Howard (crea cadenas que evalúan el valor correcto y selecciona la fuerza más corta, en lugar de la fuerza bruta, a través de las cadenas candidatas para encontrar las que evalúan el valor correcto), pero es lo suficientemente diferente como creo pertenece en una respuesta separada.

~.''':*':s@,{):x,2>{:^~$x^/~$+{s\*}x^%*}%{,}$0=}/]((=

La demostración en línea no está disponible porque ejecuta una versión con errores del intérprete.

# Let <N> denote the string which evaluates to N
# We want to enter the main loop with three values on the stack: <0> <1> <2>
# However, we'll never use <0>, so we can actually replace that with any value at all.
# Getting the input from underneath 3 items would normally use two stack manipulations.
# Trick: let's use the input value for <0>! (This gives a further bonus later).
# NB We store the value of <2> in the variable s
~.''':*':s@
# for x=1 to input_value ...
,{):x
    # for ^=2 to x-1 ...
    ,2>{:^
        # Use negative stack offsets to index the stack from the start
        # I.e. -1$ gets the first item on the stack, which is <0>
        # -2$ gets the second item on the stack, which is <1>
        # In general, val~$ gets <val>
        ~$x^/~$+
        # We have the string <^><x / ^> on the stack.
        # Increment it (x % ^) times to get a candidate <x>.
        {s\*}x^%*
    }%
    # Select a shortest string.
    {,}$0=
}/
# Group the stack into one array and select the appropriate offset,
# reusing that hacky <0> substitute for the offset.
]((=
Peter Taylor
fuente
Sería posible afeitarse uno reemplazándolo 3+con )(explotando el hecho de que []0=no deja nada en la pila) si no fuera eso []2>conduce a un error.
Peter Taylor
[]2>rinde []sin error.
Howard
@Howard, ah, golfscript.apphb.com debe estar ejecutando una versión anterior. Pero resulta que estaba equivocado, porque ese reemplazo lleva a obtener la salida incorrecta para la entrada '1'.
Peter Taylor
Que puedes arreglar con en ((=lugar de -1=.
Howard
Y golfscript.apphb.com realmente ejecuta una versión antigua, el ejemplo de bucles anidados no funciona.
Howard
4

Python 2.7 - 87 84 92

u=lambda n:n>1and min([u(i)+u(n/i)for i in range(2,n)if n%i<1]+[':'+u(n-1)+'*'],key=len)or''

Explicación:
Esta es una solución bastante sencilla. Prueba recursivamente todas las representaciones posibles de n como producto de dos números o como :(n-1)*, y luego encuentra la solución de longitud mínima. El rango (2, n) es necesario para que la recursión tenga una profundidad acotada, y n <2 da el caso base.

Notas:
i y n / i son los dos factores de n. El ... y ... o ... reemplazo de ... si ... más ... no funciona porque '' se evalúa como falso. min of strings da una de las cadenas más cortas. Python 2.7 guarda 1 carácter usando / en lugar de //.

Editar: moví el caso base a la parte posterior de la expresión, permitiéndome usar ... y ... o ... y afeitarme un par de espacios.

Casos de prueba:

u(1)
''
u(5)
'::*:**'
u(49)
'::*:*:*:*::***'
isaacg
fuente
1
" min de cadenas da una de las cadenas más cortas " no es cierto a menos que proporcione el argumento opcional key=len. Da la cadena lexicográficamente más temprana. ( Ejemplo ) Dado que '*' < ':'esto significa que tiene un sesgo hacia soluciones que involucran potencias de 2, pero ¿son siempre las más cortas?
Peter Taylor
1
Respuesta: en realidad, el sesgo es más complicado, pero no siempre da la respuesta correcta. El contraejemplo más pequeño es u(33), para el cual la clasificación lexicográfica le da 14 caracteres, ::**::*::*:***pero la clasificación por longitud le da los 12 caracteres::*:*:*:*:**
Peter Taylor
1
Nunca supe eso sobre las comparaciones de cadenas de Python. He actualizado mi respuesta.
isaacg
3

GolfScript, 63 58 56 caracteres

~n./\{:v~[':*'1$*v,,2>{v,\%!},{.v=v,@/v=+}/]{,}$0=]}*-2=

El código toma entrada en STDIN e imprime el resultado.

Ejemplos:

> 49
:::**:*:*:*:**

> 1234
::::*:*:*:**:*:*:**::**::***

Puede probar sus propios casos en línea .

Howard
fuente
Wow, pensé que un enfoque basado en factoring sería bastante más largo que un enfoque de fuerza bruta.
Peter Taylor
@PeterTaylor Yo también lo pensé pero resultó que no es el caso. Además, mi solución de fuerza bruta fue un poco más larga que la suya ;-)
Howard
¿Te importaría explicar qué hace cada porción? Solo puedo seguir hasta el momento :x(=. Además, +1 por poder ejecutar 49 en un período de tiempo razonable.
algorithmshark
@algorithmshark Todavía estoy trabajando en la solución, por lo que aún podría cambiar mucho (como acaba de hacer). Principalmente, la parte x,2>{x\%!},da todos los divisores verdaderos de x, {.v=x@/v=+}/luego concatena las soluciones para dy x/dpara todos los divisores d. {,}$los ordena por longitud y 0=toma el más corto de ellos (más el :(x-1)*caso inicial ).
Howard
2

Brachylog 2 , 30 (quizás eventualmente 26) bytes, desafío de fechas posteriores al idioma

Aquí hay una función que funciona con la implementación actual de Brachylog 2 (y devuelve una lista de códigos de caracteres porque la implementación actual tiene algunos problemas con el manejo de cadenas):

∧.l∧?{ḋp~c×ᵐ{-₁↰₁:[42,58]c↻}ᵐc}

Pruébalo en línea!

El idioma aún es muy nuevo. Aquí hay una versión de 26 bytes del programa que debería funcionar de acuerdo con la especificación, pero utiliza algunas características no implementadas y, por lo tanto, aún no es válida, pero tal vez lo sea en el futuro (también es aún menos eficiente):

{ḋp~c×ᵐ{-₁↰₁:"*:"c↻}ᵐc}ᶠlᵒh

Explicación

∧.l∧?{ḋp~c×ᵐ{-₁↰₁:[42,58]c↻}ᵐc}
∧.l∧?                            Evaluation hint: try shortest outputs first
     {                        }  Define an inner function
      ḋ                          Prime factor decomposition of the input
       p                         Find a permutation
        ~c                       Find an inverse concatenation (i.e. partition)
          ×ᵐ                     Take the product of each set inside the partition
      ḋp~c×ᵐ                     Find a decomposition into factors ≥ 2
            {              }ᵐ    For each of those factors:
             -₁                  Decrement it
               ↰₁                Call the inner function recursively
                 :[42,58]c       Append "*:" (as character codes)
                          ↻      Move the last element to the start
                             c   Append the results together

La idea básica es bastante simple: alternamos entre descomponer el número en (1 o más) factores (no necesariamente factores primos, pero los factores de 1 no están permitidos) y expresar cada uno de ellos como 1 + (una representación obtenida de un recursivo llamada). Esto garantiza la búsqueda de todas las representaciones posibles de Subcarga del número (podemos aplicar una etapa de multiplicación "dos veces seguidas" multiplicando juntos más de 2 números, y una etapa de incremento dos veces seguidas separándolos con una etapa de multiplicación que multiplica juntos solo un número). No necesitamos un caso base explícito, porque descomponer 1 en factores primos nos da una lista vacía y, por lo tanto, la construimos con una etapa de multiplicación que multiplica los números cero.

El programa es bastante ineficiente, especialmente porque la sugerencia de orden de evaluación que di (generar respuestas más cortas a más largas en términos del tamaño del resultado final), mientras resuelve la parte "más corta" de la pregunta, no es tan buena en términos de en realidad, hacer que el programa se complete rápidamente (una pista mucho más útil sería "generar solo la respuesta más corta en cada etapa recursiva", pero eso requiere más bytes ...). Además, ḋp~c×ᵐpuede generar particiones multiplicativas varias veces cada una, haciendo que el programa haga mucho trabajo redundante.


fuente
0

J - 81 char

Para la posteridad, esto fue lo mejor que pude hacer en J.

_2{::(0&(][,0{<@;"1@({~#(#~]-:"1<.)@(],.%)2}.i.@#)(/:#&>)@,':'<@,'*',~>@{:),~)&a:

Creamos una lista de resultados, comenzando con dos cadenas vacías (que es el ,~y a:) que representa 0 (nunca utilizado) y 1, y luego iteramos un verbo sobre ellos (uso furtivo de ganchos, trenes y &) que agrega la representación más corta del siguiente número.

El verbo real que iteramos utiliza la longitud de la lista como un indicador de en qué número estamos operando. Primero, factorizamos este número en pares de factores ( #(#~]-:"1<.)@(],.%)2}.i.@#), y recuperamos cada par tirando de la matriz ( {~). Convertimos cada uno de esos pares (podría haber 0 de ellos si el número es primo) en cadenas simples ( <@;"1).

Luego anexamos a esa lista la entrada para el resultado anterior entre corchetes entre :y *, y ordenamos esta lista por longitud ( (/:#&>)). Finalmente, tomamos el primer resultado de esta lista ( 0{) y lo agregamos al final de la matriz base ( [,). Cuando el ciclo termina de iterar, tenemos una lista de longitud 2 más que la entrada, comenzando en 0. Entonces, lo que tenemos que devolver es la cadena de penúltimo ( _2{::).

   un =: _2{::(0&(][,0{<@;"1@({~#(#~]-:"1<.)@(],.%)2}.i.@#)(/:#&>)@,':'<@,'*',~>@{:),~)&a:
   un 49
::*:*:*:*::***
   un 1234
:*::*:*:*::*::***::*::*:****
   un 10010
:*::*:*::***::*:*:*:*:*:*:*::***
   2 * (1 + 3 * 2^2) * (1 + 3 * 2^7)
10010
   6!:2 'un 10010'   NB. time in seconds
19.5539
Algoritmo de tiburón
fuente
0

Jelly , 33 bytes, desafío de fechas posteriores al idioma

ÆḌḊµ⁹÷Ñ;Ñð€
’ß”:;;”*W;ÇLÞḢµ“”>1$?

Pruébalo en línea!

Una solución directa de fuerza bruta.

Explicación

Programa principal

’ß”:;;”*W;ÇLÞḢµ“”>1$?
              µ  >1$?  If input is greater than 1, then:
’ß                       Run recursively on the input - 1
  ”:;                    Prepend a colon
     ;”*                 Append an asterisk
        W;               Cons to:
          Ç                the result of the helper, on {the original input}
           LÞ            Sort by length
             Ḣ           Take the first (i.e. shortest) result
               “”      Otherwise, return an empty string

El programa principal usa la función auxiliar para enumerar todas las formas posibles de producir el valor mediante la multiplicación, luego intenta producir el valor por suma y devuelve la posibilidad más corta. También maneja el caso base (una entrada de 1).

Función auxiliar

ÆḌḊµ⁹÷Ñ;Ñð€
ÆḌ µ     ð€            For all proper factors of the input
  Ḋ                    except the first (i.e. 1):
    ⁹÷                   Divide it into the input;
      Ñ                  Run the main program on it;
       ;                 Append the result of:
        Ñ                  the main program run on {the factor}

La función auxiliar intenta todos los métodos posibles de expresar la entrada como una multiplicación de dos números, y recursivamente recursivamente llama al programa principal para llegar a sus representaciones más cortas.


fuente
0

GNU Prolog, 96 bytes

v(N)-->{N#=1};{N#=A*B,A#<B,B#<N},v(A),v(B);{N#=M+1},":",v(M),"*".
s(N,S):-length(S,_),v(N,S,[]).

La primera línea es una gramática que implementa la evaluación de baja carga y funciona en la dirección inversa (en realidad, no funciona en la dirección hacia adelante debido a la A#<Brestricción; cámbielo a A#<Nun programa más lento que funcione en ambos sentidos). La segunda línea define el predicado similar a una función s(que es la función que se implementa como una solución para este programa) que encuentra la cadena más corta posible que evalúa el número dado como entrada (esto es frustrantemente detallado para lo que es una tarea relativamente simple, pero eso es Prolog para ti ...).

El programa debería explicarse por sí mismo, dado que es más o menos una traducción directa de la especificación a una gramática y luego a la sintaxis de Prolog; la definición de vdice que Nes 1 en una cadena vacía, o Nes A× B(con Amenos de Bmenos que N) y la cadena es la concatenación de v(A)y v(B), o Nes M+ 1 y la cadena se :concatena con v(M)concatenada con *. La segunda línea es un poco más sutil;length(S,_) significa "S tiene algo de longitud", pero especificar esto como lo primero en la línea actúa como una pista para la implementación de Prolog de que primero debe verificar las longitudes más cortas (lo que significa que obtendremos la longitud más corta posible para un valor de retorno) .


fuente