Curva ASCII Hilbert

23

Dado un nresultado entero , la niteración de la curva de Hilbert en ASCII utilizando los caracteres _y |.

Aquí están las primeras 4 iteraciones:

n=1
 _ 
| |

n=2
 _   _ 
| |_| |
|_   _|
 _| |_

n=3
 _   _   _   _ 
| |_| | | |_| |
|_   _| |_   _|
 _| |_____| |_ 
|  ___   ___  |
|_|  _| |_  |_|
 _  |_   _|  _ 
| |___| |___| |

n=4
 _   _   _   _   _   _   _   _ 
| |_| | | |_| | | |_| | | |_| |
|_   _| |_   _| |_   _| |_   _|
 _| |_____| |_   _| |_____| |_ 
|  ___   ___  | |  ___   ___  |
|_|  _| |_  |_| |_|  _| |_  |_|
 _  |_   _|  _   _  |_   _|  _ 
| |___| |___| |_| |___| |___| |
|_   ___   ___   ___   ___   _|
 _| |_  |_|  _| |_  |_|  _| |_ 
|  _  |  _  |_   _|  _  |  _  |
|_| |_| | |___| |___| | |_| |_|
 _   _  |  ___   ___  |  _   _ 
| |_| | |_|  _| |_  |_| | |_| |
|_   _|  _  |_   _|  _  |_   _|
 _| |___| |___| |___| |___| |_ 

Aclaraciones

  • Mi pregunta es similar a Dibujar la curva de Hilbert y Dibujar la curva de Hilbert usando barras inclinadas .
  • La conversión entre guiones bajos ( _) y barras verticales ( |) es u=2*v-1donde uestá el número de _sy ves el número de |s.
  • Para mantener la coherencia con mi publicación original, la curva debe comenzar y terminar en la parte inferior.
  • Puede tener un programa completo o una función.
  • Salida a stdout (o algo similar).
  • Puede tener espacios en blanco iniciales o finales, la salida solo necesita alinearse para que se vea como los ejemplos.
  • Este es el código de golf, por lo que la respuesta más corta en bytes gana.
Bobas_Pett
fuente
3
¿Podría incluir una definición de la curva de Hilbert en su publicación y las especificaciones exactas de cómo se construye la versión ASCII?
Loovjo
@Bobas_Pett: No kolmogorov-complex
shooqie
@shooqie hay un debate sobre eso en meta
trichoplax
@Loovjo Agregué un punto sobre las longitudes de los guiones bajos (_) y barras verticales (|) en "Aclaraciones", si aún se necesita más información o una definición rigurosa, por favor dígame.
Bobas_Pett
@shooqie eliminé la etiqueta
Bobas_Pett

Respuestas:

5

Befunge, 444 368 323 bytes

&1>\1-:v
0v^*2\<_$00p>
_>:10p\:20pv^_@#-*2g00:+1,+55$
^!-<v*2g000<>$#<0>>-\:v
g2*^>>10g20g+v \ ^*84g_$:88+g,89+g,\1+:00
v#*!-1g02!g01_4^2_
>::00g2*-!\1-:10g-\20g-++>v
87+#^\#p01#<<v!`g01/2\+76:_
vv1-^#1-g01:\_$:2/20g`!
_ 2/^>:10g#vv#`g02/4*3:\+77
v>0p^^/2:/2_
<^2-1-g02</2`#*3:
0g+10p2*:^*3_1
! "#%$
%$"#!
 !!##%
|||_
 _ __

Pruébalo en línea!

El enfoque típico para dibujar la curva de Hilbert es seguir la ruta como una serie de trazos y vueltas, renderizando el resultado en un mapa de bits o alguna área de memoria, y luego escribiendo esa representación cuando se completa la ruta. Esto simplemente no es factible en Befunge cuando solo tenemos 2000 bytes de memoria para trabajar, y eso incluye la fuente del programa en sí.

Entonces, el enfoque que hemos tomado aquí es crear una fórmula que nos diga exactamente qué carácter generar para una determinada coordenada x, y. Para entender cómo funciona esto, es más fácil hacer caso omiso de la prestación ASCII para empezar, y sólo pensar en la curva como formado por caracteres de cuadro: , , , , , y .

Cuando miramos la curva así, podemos ver de inmediato que el lado derecho es un espejo exacto del lado izquierdo. Los caracteres a la derecha se pueden determinar simplemente mirando a su compañero a la izquierda y reflejándolo horizontalmente (es decir, las ocurrencias de y se intercambian, como son y ).

Curva de Hilbert de nivel 3 que muestra la reflexión a través del eje vertical

Luego, mirando la esquina inferior izquierda, nuevamente podemos ver que la mitad inferior es un reflejo de la mitad superior. Por lo tanto, los caracteres en la parte inferior se determinan simplemente al buscar a su compañero arriba y reflejarlo verticalmente (es decir, las ocurrencias de y se intercambian, como son y ).

Curva de Hilbert de nivel 3 que muestra el reflejo a través del eje horizontal en la esquina inferior izquierda

La mitad restante de esta esquina es un poco menos obvia. El bloque de la derecha se puede derivar de una reflexión vertical del bloque diagonalmente adyacente a él.

Curva de Hilbert de nivel 3 que muestra cómo se puede derivar el bloque superior derecho de la esquina inferior izquierda

Y el bloque de la izquierda puede derivarse de una reflexión vertical del bloque en la esquina superior izquierda de la curva completa.

Curva de Hilbert de nivel 3 que muestra cómo se puede derivar el bloque superior izquierdo de la esquina inferior izquierda

En este punto, todo lo que nos queda es la esquina superior izquierda, que es solo otra curva de Hilbert una iteración más baja. En teoría, ahora solo deberíamos repetir el proceso nuevamente, pero hay un problema: en este nivel, las mitades izquierda y derecha del bloque no son espejos exactos entre sí.

Entonces, en cualquier otra cosa que no sea el nivel superior, los caracteres de la esquina inferior deben manejarse como un caso especial, donde el personaje se refleja como , y el personaje se refleja como .

Curva de Hilbert de nivel 3 que muestra cómo se puede derivar el bloque superior izquierdo de la esquina inferior izquierda

Pero aparte de eso, realmente podemos repetir este proceso de forma recursiva. En el último nivel codificamos el carácter superior izquierdo como , y el carácter inferior como .

Secuencia de imágenes que muestran cómo se derivan las partes restantes de la curva

Ahora que tenemos una manera de determinar la forma de la curva en una coordenada x, y particular, ¿cómo traducimos eso a la representación ASCII? En realidad, es solo un mapeo simple que traduce cada posible mosaico en dos caracteres ASCII.

  • se convierte  _(espacio más guión bajo)
  • se convierte   (dos espacios)
  • se convierte |_(barra vertical más guión bajo)
  • se convierte (barra vertical más espacio)
  • se convierte (nuevamente una barra vertical más espacio)
  • se convierte __(dos guiones bajos)

Esta asignación no es intuitiva al principio, pero puede ver cómo funciona cuando mira dos representaciones correspondientes una al lado de la otra.

Nivel 2 Hilbert Curve representada como arte ASCII y con caracteres de cuadro

Y eso es básicamente todo lo que hay que hacer. En realidad, implementar este algoritmo en Befunge es otro problema por completo, pero dejaré esa explicación para otro momento.

James Holderness
fuente
2

C, 267 bytes

const n=4,s=1<<n,a[]={1,-s,-1,s};l,p,q;
t(d){d&=3;p-q||printf("%.2s",&"__|      _|       |___ _|_| | | "[l*8+d*2]);p+=a[l=d];}
h(d,r,n){n--&&(h(d+r,-r,n),t(d+r),h(d,r,n),t(d),h(d,r,n),t(d-r),h(d-r,-r,n));}
main(){for(;p=s*s-s,l=1,q<s*s;++q%s||putchar(10))h(0,1,n),t(3);}

Pruébalo en línea!

h()usa la recursividad para generar los trazos de la curva hlibert. t()solo imprime el carácter de trazo si la posición del lápiz pes igual a la posición de salida actualq .

Esto es ineficiente pero simple.

Si la curva comienza en la esquina superior izquierda, el código puede reducirse a 256 bytes.

Milo Yip
fuente
Sugerir en puts("")lugar de putchar(10)y en "..."+l*8+d*2lugar de &"..."[l*8+d*2]y en n--?h(d+r...-r,n):0lugar den--&&(h(d+r...-r,n))
ceilingcat