Asignar cadena a curva de Hilbert

27

Asignemos algunas cadenas al espacio 2d, estilo fractal. Su tarea es calcular una curva de Hilbert y colocar una cadena a lo largo de ella.

La curva de Hilbert, iteraciones 1 a 8

Tarea

La tarea es tomar la cadena de entrada de una sola línea y colocarla a lo largo de una curva de Hilbert lo suficientemente grande como para contenerla, pero no más grande. Intente hacer que el byte cuente lo más bajo posible; ¡Esto es después de todo!

Condiciones

  • Cualquier espacio que deba rellenarse con espacios en blanco, pero no se requiere relleno al final de las líneas.
  • El inicio de la línea debe estar en la esquina superior izquierda y el final en la esquina inferior izquierda.
  • Puede crear un programa o función.
  • Es posible que aparezcan nuevos casos de prueba, ¡así que no codifiques nada!

Bonos

Nota: Los bonos se acumulan así: -50% & -20% on 100B= -20% on 50Bo -50% on 80B= 40B.

  • -50% Si la entrada es una cadena de varias líneas, invierta el proceso para crear la entrada original. Casos de prueba para la bonificación: solo use los existentes (¡incluidos los casos de prueba de bonificación!)
  • -20% Si elimina todos los espacios en blanco innecesarios de la salida (por ejemplo, al final de una línea).
  • -5% Si no contamina el espacio de nombres global (¡ya sabe a qué me refiero!)

Casos de prueba

abcdefghijklmn

adef
bchg
 nij
 mlk


The quick brown fox jumps over the lazy dog.

Thn f ju
 ewooxpm
qckr rs 
ui btevo
    hlaz
    e  y
      do
      .g

Y para el bono de eliminación de espacios en blanco:

No  hitespac  her 

Noher

hesc
itpa

Tabla de clasificación

Para asegurarse de que su respuesta se muestre, comience con un título, utilizando la siguiente plantilla de Markdown:

# Language Name, N bytes

¿Dónde Nestá el tamaño de su envío? Si mejora su puntaje, puede mantener los puntajes antiguos en el título, tachándolos. Por ejemplo:

# Ruby, <s>104</s> <s>101</s> 96 bytes

Si desea incluir varios números en su encabezado (por ejemplo, porque su puntaje es la suma de dos archivos o desea enumerar las penalizaciones de la bandera del intérprete por separado), asegúrese de que el puntaje real sea el último número en el encabezado:

# Perl, 43 + 2 (-p flag) = 45 bytes

También puede hacer que el nombre del idioma sea un enlace que luego aparecerá en el fragmento de la tabla de clasificación:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes
wizzwizz4
fuente
Si alguien puede hacer más casos de prueba, eso sería apreciado.
wizzwizz4
Entonces, ¿las características deberían estar representadas por vértices de la curva?
defecto
No..hitespac..her.donde los puntos son espacios sería un mejor caso de prueba para la bonificación. (Y actualmente, el caso de prueba no tiene el seguimiento .)
Martin Ender
Si está adoptando el enfoque del sistema L, es posible que también desee probar http: // codegolf / preguntas / 48697 / ascii-l-system-renderer . Podría ayudarte a desarrollar tus respuestas.
wizzwizz4

Respuestas:

7

CJAM, 119 117 113 112 109 * 0.5 * 0.8 = 43.6 bytes

Gracias a Dennis por guardar 1 byte.

Aquí hay un comienzo ...

{+e`W<e~}:F;q_N-,4mLm]0aa{4\#4e!1=f*\:G[zGGW%zW%G].ff+2/{~.+~}%}@:L/\_N&{N/]z:z:~$1f>sS}{4L#' e]f{f=SF}N*N}?F

Prueba la transformación directa . Prueba la transformación inversa.

Estoy seguro de que hay una forma más corta de generar la curva ...

Explicación

Primero, defino una función para recortar algún elemento desde el final de una matriz, porque lo necesito en varios lugares. Espera la matriz y el elemento (dentro de una matriz separada) en la parte superior de la pila.

{
  +  e# Append the element to the array.
  e` e# Run-length encode.
  W< e# Discard last run.
  e~ e# Run-length decode.
}:F; e# Store in F and discard.

Ahora, la mayoría del código determina el tamaño de la curva de Hilbert requerida y la construye como una matriz 2D donde los elementos son índices a lo largo de la curva. Construyo esto basado en la siguiente observación:

Considere la curva de Hilbert 2x2:

01
32

La curva de Hilbert 4x4 es:

0345
1276
ed89
fcba

Si restamos el valor mínimo de cada cuadrante (y los separamos un poco para mayor claridad visual), obtenemos:

03 01
12 32

21 01
30 32

Este patrón es válido para cualquier tamaño. Significa que podemos construir el siguiente nivel a partir del actual, utilizando como los cuatro cuadrantes: a) la transposición del nivel actual, b) el nivel actual en sí, c) la transposición a lo largo de la diagonal, d) nuevamente El nivel actual en sí. Y luego los compensamos 0, 1, 3, 2 veces el tamaño del nivel actual, respectivamente.

q           e# Read input.
_N-         e# Make a copy and remove all linefeeds.
,4mLm]      e# Take that string's length's logarithm with base 4, rounded up.
            e# This is the Hilbert curve level we need.
0aa         e# Push [[0]] as the level-0 Hilbert curve.
{           e# Store the Hilbert curve level in L. Then for each i from 0 to L-1...
  4\#       e#   Compute 4^i. This is the offset of the four quadrants.
  4e!1=     e#   Get [0 1 3 2] as the second permutation returned by 4e!.
  f*        e#   Multiply each of them by the offset.
  \:G       e#   Swap with the Hilbert curve so far and call it G.
  [         e#   Create an array with...
    z       e#     The transpose of G.
    G       e#     G itself.
    GW%zW%  e#     The anti-diagonal transpose of G.
    G       e#     G itself.
  ]
  .ff+      e#   Add the appropriate offsets to the indices in each of the four quadrants.
  2/        e# Split into a 2x2 grid.
  {         e# Map this onto each pair of quadrants...
    ~       e#   Dump both quadrants on the stack.
    .+      e#   Concatenate them line by line.
    ~       e#   Dump the lines on the stack.
  }%        e# Since this is a map, the lines will automatically be collected in an array.
}@:L/

Finalmente, usamos esta curva de índices de Hilbert para aplicar la transformación apropiada a la entrada:

\_        e# Swap the curve with the input and make another copy.
N&{       e# If the input contains linefeeds, execute the first block, else the second...
  N/      e#   Split the input into lines. The stack now has a grid of indices and a grid
          e#   of characters.
  ]z:z:~  e#   This is some weird transposition magic which zips up the indices with the
          e#   corresponding characters from both grids, and finally flattens the grid
          e#   into a linear list of index/character pairs. Those cells that don't have
          e#   characters due to trimmed whitespace in the input will be turned into
          e#   arrays containing only an index.
  $       e#   Sort the pairs (which sorts them by indices).
  1f>     e#   Discard the indices.
  s       e#   Flatten the result into a single string.
  S       e#   Leave a space on the stack to be trim trailing spaces later.
}{        e# or...
  4L#     e#   Compute the size of the Hilbert curve.
  ' e]    e#   Pad the input to that size with spaces.
  f{      e#   Map this block over lines of the curve, passing the padding input as an
          e#   additional parameter...
    f=    e#     For each index in the current line, select the appropriate character
          e#     from the padded input.
    SF    e#     Trim spaces from the end of the line.
  }
  N*      e#   Join the lines with linefeed characters.
  N       e#   Leave a linefeed on the stack to be trim trailing linefeeds later.
}?
F         e# We left either a space or a linefeed on stack... trim that character from
          e# the end of the string.
Martin Ender
fuente
3

Python 3, 467 434 423 457 451 426 386 374 342 291 304 * 80% * 95% = 231.04 bytes

La forma en que esto funciona es que hago la curva de Hilbert usando un sistema Lindenmayer y sigo las instrucciones izquierda, derecha y hacia adelante a lo largo de una serie de cadenas. Sin embargo, probablemente hay muchas maneras en que esto podría jugar mejor; especialmente en los condicionales y en la formación de la serie de cuerdas. (Intenté [" "*p for i in range(p)]pero las cadenas no admiten la asignación de elementos (aparentemente). Si pudiera hacer que eso funcione, también podría deshacerme de la unión)

Editar: Golfé algunos de los condicionales gracias a Dennis . Y jugué golf en el conjunto de cuerdas. Y un cambio sin byte porque los resultados salían transpuestos en comparación con los ejemplos anteriores.

Editar: implementado el bono de eliminación de espacios en blanco.

Editar: se corrigió un error en mi código de eliminación de espacios en blanco por seis bytes más

Editar: como esta respuesta no contamina el espacio de nombres global, obtengo el 5% de bonificación, de acuerdo con wizzwizz4 aquí .

Editar: se modificó cómo gse incrementa y disminuye. Ahora usando eval()y str.translate.

Editar: esta respuesta ahora es un programa en lugar de una función.

Editar: se corrigieron algunos errores del golf anterior.

s=input();m=(len(bin(len(s)-1))-1)//2;t=eval("[' ']*2**m,"*2**m);t[0][0],*s=s;x=y=g=0;b="A";exec("b=b.translate({65:'-BF+AFA+FB-',66:'+AF-BFB-FA+'});"*m)
while s:
 c,*b=b;g+=(c<"-")-(c=="-")
 if"B"<c:x,y=[[x+1-g%4,y],[x,y+g%4-2]][g%2];t[x][y],*s=s
print("\n".join(''.join(i).rstrip()for i in t).rstrip())

Sin golf:

# hilbert(it) is now implemented in the code with exec("b=b.translate")

def hilbert(it):
    s="A"
    n=""
    for i in range(it):
        for c in s:
            if c == "A":
                n += "-BF+AFA+FB-"
            elif c == "B":
                n += "+AF-BFB-FA+"
            else:
                n += c
        s=n;n=""
    return s

def string_to_hilbert(string):
    length = len(string)
    it = (len(bin(length-1))-1)//2
    hil = hilbert(it)
    pow_2 = 2**it
    # we use eval("[' ']*pow_2,"*pow_2) in the code, but the following is equivalent
    output = [[" "for j in range(pow_2)] for i in range(pow_2)]
    output[0][0] = string[0]
    x = 0
    y = 0
    heading = 0
    while string: # while there are still characters in string
        char, *hil = hil
        if char == "-": heading = heading - 1
        elif char == "+": heading = heading + 1
        elif char == "F":
            if heading % 4 == 3: y += 1
            elif heading % 4 == 2: x -= 1
            elif heading % 4 == 1: y -= 1
            else: x += 1
            output[x][y], *string = string
    array = [''.join(i).rstrip()for i in output]
    array = "\n".join(array).rstrip()
    print(array)
    return
Sherlock9
fuente
Curioso sobre la bonificación del 5%. ¿Son las variables automáticamente locales en Python?
edc65
@ edc65 Le pregunté al escritor del desafío algo similar aquí: chat.stackexchange.com/transcript/240?m=28529277#28529277 . Espero que esto ayude un poco. Si no, podemos continuar la discusión en el chat.
Sherlock9
2

Ruby, 358 356 344 322 319 * 80% * 95% = 242.44 bytes

Este es mi código Python transpilado a Ruby. Debería escribir más respuestas en Ruby. Es un lenguaje decente para jugar golf.

Editar: Olvidé que las funciones no necesitan ser nombradas en esta pregunta.

Editar: como esta respuesta no contamina el espacio de nombres global, obtengo el 5% de bonificación, de acuerdo con wizzwizz4 aquí .

->s{l=s.size;m=((l-1).bit_length+1)/2;x=2**m;t=(1..x).map{[" "]*x};t[0][0]=s[0];x=y=g=z=0;d=1;b=?A;m.times{b=b.split("").map{|c|c==?A?"-BF+AFA+FB-":c==?B?"+AF-BFB-FA+":c}.join("")};(c=b[z];z+=1;g+=c<?-?1:c==?-?-1:0;(g%2>0?y+=g%4-2:x+=1-g%4;t[x][y]=s[d];d+=1)if c>?B)while d<l;puts (t.map{|i|(i*'').rstrip}*"\n").rstrip}

Sin golf:

def map_string(string)
  len = string.size
  m = ((len-1).bit_length+1)/2
  pow = 2**m
  output = (1..pow).map{[" "]*pow}
  output[0][0] = s[0]
  x = y = heading = char_index = 0
  chars_in_output = 1
  b = ?A
  m.times do |j|
    a = b.split("").map do |char|
      if char == "A"
        "-BF+AFA+FB-"
      else if char == "B"
        "+AF-BFB-FA+"
      else
        char
      end
    end
    b = a.join("")
  end
  while chars_in_output < len
    char = b[char_index]
    char_index += 1
    if char == "-"
      heading += -1
    else if char == "+"
      heading += 1
    else if char == "F"
      if heading % 2 == 0
        y += heading % 4 - 2
      else
        x += 1 - heading % 4
      end
    end
    output[x][y] = string[char_index]
    char_index += 1
  end
  return (output.map{|i|(i*'').rstrip}*"\n").rstrip
Sherlock9
fuente
¿Este código tiene doble licencia bajo una licencia de código? Me gustaría producir un trabajo derivado que se publique bajo la GPL (aunque cualquier licencia compatible con GPL funcionará con esto). Actualmente se publica bajo CC BY-SA 3.0.
wizzwizz4
@ wizzwizz4 Chatee aquí: chat.stackexchange.com/rooms/56405/…
Sherlock9
1

JavaScript (ES6), 227 - 20%: 181,6 bytes

m=>{for(n=1<<((33-Math.clz32(m.length-1))/2),t='',y=0;y<n;y++,t+=`
`)for(x=0;x<n;x++,t+=m[h]||' ')for(u=y,v=x,h=0,s=n;s>>=1;q||(p&&(u=s+~u,v=s+~v),[u,v]=[v,u]))h+=s*s*(3*!!(p=u&s)^!!(q=v&s));return t.replace(/ +$/mg,'').trim()}

Tratando de obtener el 5% de bonificación

m=>{for(var n=1<<((33-Math.clz32(m.length-1))/2),t='',x,y=0;y<n;y++,t+=`
`)for(x=0;x<n;x++,t+=m[h]||' ')for(var p,q,u=y,v=x,h=0,s=n;s>>=1;q||(p&&(u=s+~u,v=s+~v),[u,v]=[v,u]))h+=s*s*(3*!!(p=u&s)^!!(q=v&s));return t.replace(/ +$/mg,'').trim()}

241 * 0.8 * 0.95: 183.16 más grande

Menos golf

m=>
{
  // calc the size of the bounding square, clz32 is a bit shorter than ceil(log2()
  n = 1<<( (33-Math.clz32(m.length-1)) / 2); 
  t = '';
  for(y = 0; y < n; y++) 
  {
    for(x = 0 ; x < n; x++)
    {
      // for each position x,y inside the square
      // get the index postion in the hilbert curve
      // see https://en.wikipedia.org/wiki/Hilbert_curve (convert x,y to d)
      for(u=y, v=x, h=0, s=n; s >>= 1; )
      {
        h += s*s*(3 * !!(p = u & s) ^ !!(q = v & s));
        q || (p && (u = s+~u, v = s+~v),[u,v]=[v,u])
      }
      // add char at given index to output  
      t += m[h]||' '; // blank if beyond the length of m
    }
    t += '\n'; // add newline add end line
  }
  return t.replace(/ +$/mg,'').trim() // to get the 20% bonus
}  

Prueba

F=m=>{for(n=1<<((33-Math.clz32(m.length-1))/2),t='',y=0;y<n;y++,t+=`
`)for(x=0;x<n;x++,t+=m[h]||' ')for(u=y,v=x,h=0,s=n;s>>=1;q||(p&&(u=s+~u,v=s+~v),[u,v]=[v,u]))h+=s*s*(3*!!(p=u&s)^!!(q=v&s));return t.replace(/ +$/mg,'').trim()}

function Test() { O.textContent = F(I.value) }

Test()
#I { width: 90% }
#O { border: 1px solid #ccc}
<input id=I oninput='Test()' value='The quick brown fox jumps over the lazy dog.'>
<pre id=O></pre>

edc65
fuente
¿Valdría la pena agregar vars para obtener el 5% de bonificación?
wizzwizz4
var s,x,y,u,v,t,p,q,n,hno, no vale la pena @ wizzwizz4
edc65
Puedes poner varantes del primer uso de cada ... Oh, eso es aún peor.
wizzwizz4
@ wizzwizz4 en general, tal vez tengas un punto ... estoy intentando ... no. Lástima
edc65