Imprima una espiral ascii en la memoria O (log n)

13

Puede escribir un programa o función que reciba un número entero impar y positivo n , donde n >= 3, como argumento de función, argumentos de línea de comando o en STDIN (o equivalente para su sistema), e imprima en STDOUT (o equivalente de sistema) una espiral ASCII que gira hacia adentro en el sentido de las agujas del reloj donde el borde superior tiene exactamente una nlongitud de caracteres. El primer borde derecho debe tener n+1caracteres largos, obviamente. Por ejemplo,

Entrada:

11

Salida:

***********
          *
********* *
*       * *
* ***** * *
* *   * * *
* * * * * *
* * *** * *
* *     * *
* ******* *
*         *
***********

Las capturas:

  • Su programa no debe usar más que O(log n)memoria .
  • Su programa solo puede imprimir los caracteres *(ASCII 42), (ASCII 32), <CR>(ASCII 13) y <LF>(ASCII 10).
  • Su programa debe imprimir la cadena, no devolverla de la función.
  • La restricción Big-O solo está en la memoria , no hay restricciones en el tiempo de ejecución .
  • Una nueva línea final es opcional.
  • Si su idioma no admite tipos enteros grandes, no tiene que admitir más de lo que admite, pero no puede usar esto como un truco para decir "oh, bueno, no tengo que admitir por encima de X, así que puede hacer que una gran variedad sea el tamaño máximo cada vez "

Las lagunas estándar están prohibidas, como de costumbre.

durron597
fuente
2
No creo que esto sea posible. No se puede almacenar la entrada nen la memoria O (1).
xnor
@xnor "O (1) constituye un uso constante de memoria. Por lo tanto, la cantidad de entrada es intrascendente" - Si la entrada n encaja en un entero, entonces estoy seguro de que puede codificarse en un uso constante de memoria.
André
1
Almacenar la entrada ntoma log nbits. A medida que ncrece, también lo hace el espacio necesario para almacenarlo. ¿Tal vez estás diciendo que hagas esto con un número limitado de variables?
xnor
O, alternativamente, ¿hay un límite n?
Sp3000
Creo que está diciendo que no puede almacenar toda la salida a la vez, luego simplemente imprimirla de una vez porque eso se agrandará. Probablemente tenga que imprimirlo recursivamente.
Jacob

Respuestas:

9

C, 125 121 bytes

Versión de golf Esto no tiene variable k. La variable kse usa en la versión sin golf solo para facilitar la legibilidad. También forse reacomodan los condicionales de bucle y se {}elimina un conjunto de innecesarios . Se {}puede eliminar otro conjunto migrando puts("")dentro de los corchetes del jbucle en la posición de inicialización, pero esto significaría una nueva línea al comienzo de la salida, por lo que no lo he hecho.

f(n){int i,j;n/=2;for(i=-n-2;i++-n-1;){if(i){for(j=-n-1;j++-n;)putchar(32+10*(n+(j*j<i*i?i:j+(i!=j|i>0))&1));puts("");}}}

Imprime una espiral nancha por n+1alta como el ejemplo.

Explicación

Básicamente, reducir a la mitad el valor de n(redondeando hacia abajo) y corro dos bucles: una exterior ia partir -n/2-1de n/2+1imprimir las filas ( i=0se suprime por lo que obtener n+1filas) y uno internoj a partir de ( -n/2a n/2Utilizamos para imprimir los caracteres.) expression & 1Para imprimir rayas y la condición j*j<i*ipara decidir si se imprimen rayas verticales u horizontales (vertical en los lados donde la magnitud absoluta ies mayor y horizontal en la parte superior e inferior). Se +nrequiere un ajuste para ayudar con la terminación correcta dependiendo de sin/2 es impar o no. incluso.

k normalmente es 1 y proporciona un ajuste por el hecho de que los valores absolutos de i rango de 1 a n/2+1mientras que los valores absolutos de jrango de 0 a n/2. Si ksiempre fuera 1 obtendríamos rectángulos concéntricos, pero se invierte a 0 cuando se invierte i==j&i<=0una fila diagonal de celdas, produciendo la espiral.

sin golf en el programa de prueba

f(n){
  int i,j,k;
  n/=2;
  for(i=-n-1;i<=n+1;i++){
    if(i){
       for(j=-n;j<=n;j++){
           k=i!=j|i>0;
           putchar(32+10*(n+(j*j<i*i?i:k+j)&1));
         }
       puts("");
     }
  }
} 

int m;
main(){
  scanf("%d",&m);
  f(m);
}

Salida

11
***********
          *
********* *
*       * *
* ***** * *
* *   * * *
* * * * * *
* * *** * *
* *     * *
* ******* *
*         *
***********

9
*********
        *
******* *
*     * *
* *** * *
* * * * *
* *   * *
* ***** *
*       *
*********

3
***
  *
* *
***

1
*
*
Level River St
fuente
Golpéame un poco ... +1 ¡esto es muy corto!
sudo rm -rf slash
1
114 bytes
ceilingcat el
7

C, 118 bytes

m,p,x,y,d;f(n){for(m=n++/2;p<n*n;x=p%n-m,y=p++/n-m,d=y==x+1&x<0,y-=y>0,d+=x*x>y*y?x:y,putchar(x>m?10:(d+m)%2?32:42));}

Código antes del golf final:

#include <stdio.h>

int m, p, x, y, d;

int f(int n) {
    for (m = n++ / 2; p < n * n; ) {
        x = p % n - m;
        y = p++ / n - m;
        d = y == x + 1 && x < 0;
        y -= y > 0;
        d += x * x > y * y ? x : y;
        if (x > m) {
            putchar(10);
        } else if ((d + m) % 2) {
            putchar(32);
        } else {
            putchar(42);
        }
    }

    return 0;
}

La observación clave es que el patrón es casi una serie de cuadrados concéntricos. Con un par de pequeñas arrugas:

  • El tamaño y es uno más grande que el tamaño x. Esto se corrige restando 1 de y para la mitad inferior, que esencialmente repite la fila del medio.
  • Para convertir los rectángulos en una espiral, los píxeles a lo largo del y = x + 1 diagonal deben invertirse hasta la mitad de la forma.

Por lo demás, el código simplemente se repite en todas las posiciones, calcula la distancia de Chebyshev desde el centro para cada posición y emite uno de los dos caracteres dependiendo de si la distancia es par o impar. Y emitiendo una nueva línea para la última posición de cada línea.

Como solo hay unas pocas variables escalares y los caracteres se emiten uno por uno, el uso de la memoria es obviamente constante.

Reto Koradi
fuente
Excelente respuesta, pero como no se inicializa p, creo que no cumple con meta.codegolf.stackexchange.com/q/4939/15599 . Tampoco estoy seguro de declarar variables globales al enviar una función. Obviamente mi respuesta sería 4 bytes más corta si hiciera esto. Empecé una meta publicación meta.codegolf.stackexchange.com/q/5532/15599
Level River St
Sí, se me ocurrió que probablemente debería inicializar p.
Reto Koradi
3

C ++, 926 bytes

#include<iostream>
#include<string>
#include<math.h>
#define S string
using namespace std;S N(S x,int y){S z="";for(int q=0;q<y;q++){z+=x;}return z;}int main(){int n=0,t=0,g=0,fi=1;cin>>n;int t1[]={0,0,n,0};int t2[]={0,n-2,n-2,1};for(int k=0;k<n+1;k++){if((k>(n-2)/2)&&(k<(n+5)/2)){if(g==0){S d,e;if(!((n+1)%4)){cout<<N("* ",t2[0])<<"  *"<<N(" *",t2[0])<<endl<<N("* ",(n+1)/2)<<endl<<N("* ",t2[0])<<"***"<<N(" *",t2[0])<<endl;t2[2]=n-8-(n-11);t1[2]=n-4-(n-11);t1[0]--;t2[3]--;t1[3]-=2;}else{cout<<N("* ",t1[0])<<"***"<<N(" *",t2[0])<<endl<<N("* ",(n+1)/2)<<endl<<N("* ",t1[0])<<"*  "<<N(" *",t2[0])<<endl;t2[0]--;t1[2]+=2;t2[2]+=6;t1[3]--;t2[1]-=2;t2[3]-=2;}fi=0;}g=5;}else{t=1-t;int*tR;tR=t?t1:t2;cout<<N("* ",tR[0])<<N(t?"*":" ",tR[2])<<N(" *",tR[3])<<endl;if(fi){if(t){t1[0]+=k==0?0:1;t1[2]-=k==0?2:4;t1[3]++;}else{t2[0]++;t2[2]-=4;t2[3]++;}}else{if(t){t1[0]--;t1[2]+=4;t1[3]--;}else{t2[0]--;t2[2]+=4;t2[3]--;}}}}return 0;}

Esto no es elegante, pero no ocupa mucha memoria para grandes n. Además, hay (casi con certeza) alrededor de 20 personajes que se pueden jugar más, pero no puedo soportar verlo más.

Breve explicación:

Esto divide las líneas en las espirales en dos tipos: las que tienen ****** en el medio y las que tienen \ s \ s \ s \ s \ s en el medio. Entonces está claro que cada línea está compuesta por varios "*", el medio y algunos "*". Averiguar exactamente cuántas de cada cosa es simple si observa el patrón durante el tiempo suficiente. Lo complicado fue imprimir el centro de la espiral, que básicamente codifiqué usando un condicional. Esto terminó siendo útil porque las líneas *** y \ s \ s \ s cambian siendo impares / pares allí.

Pruebas:

Entrada: 55 (creo que los grandes se ven más geniales)

Salida:

************************************************** *****
                                                      * *
************************************************** *** *
* * *
* ************************************************* * *
* * * * *
* * ********************************************* * * *
* * * * * * *
* * * ***************************************** * * * *
* * * * * * * * *
* * * * ************************************* * * * * *
* * * * * * * * * * *
* * * * * ********************************* * * * * * *
* * * * * * * * * * * * *
* * * * * * ***************************** * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * ************************* * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * * * ********************* * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * * * ***************** * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ************* * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * ********* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ***** * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * {- mi programa agrega un espacio aquí por cierto
* * * * * * * * * * * * * *** * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ******* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * *********** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * *************** * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * ******************* * * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * * *********************** * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * *************************** * * * * * * *
* * * * * * * * * * * * * *
* * * * * * ******************************* * * * * * *
* * * * * * * * * * * *
* * * * * *********************************** * * * * *
* * * * * * * * * *
* * * * *************************************** * * * *
* * * * * * * *
* * * ******************************************* * * *
* * * * * *
* * *********************************************** * * *
* * * *
* ************************************************* ** *
* *
************************************************** *****

Entrada: 3

Salida:

***
  * *
* * 
***

Nota: No soy un estudiante de informática / CS, y no sé cómo demostrar que esto usa memoria O (log n). Solo puedo averiguar qué hacer en función de los enlaces de la pregunta. Estaría agradecido si alguien pudiera confirmar / negar si esta respuesta es válida. Mi lógica para la validez de esta respuesta es que nunca almacena ninguna variable de tamaño basada en n, excepto la entrada en sí. En cambio, un bucle for que se ejecuta n veces calcula valores enteros basados ​​en n. Hay el mismo número de esos valores independientemente de la entrada.

Nota 2: Esto no funciona para n = 1 debido a mi método de tratar con el medio. Esto sería fácil de solucionar con condicionales, así que si alguien está dentro de unos pocos caracteres de mi respuesta, lo arreglaré;)

Juega con él en ideone.

sudo rm -rf slash
fuente
Creo que es válido, a pesar de que este código C ++ en una sola línea tiene que leerse. ;) Su comprensión es correcta. No puede usar ninguna memoria con un tamaño que dependa de n. Un ejemplo típico que no cumple con el requisito sería algún tipo de cadena / búfer / matriz que contenga una línea completa de salida.
Reto Koradi
Dado que esto es la única respuesta, he ajustado la pregunta para que no requiera manejo n=1, ya que no considero que una carcasa especial sea interesante.
durron597
3

Haskell, 151 bytes

(#)=mod
f n=[[if y<= -(abs$x+1)||y>abs x then r$y#2/=n#2 else r$x#2==n#2|x<-[-n..n]]|y<-[-n-1..n+1],y/=0]
r b|b='*'|1<2=' '
p=putStr.unlines.f.(`div`2)

Ejemplo de uso:

*Main> p 9
*********
        *
******* *
*     * *
* *** * *
* * * * *
* *   * *
* ***** *
*       *
*********

*Main> p 11
***********
          *
********* *
*       * *
* ***** * *
* *   * * *
* * * * * *
* * *** * *
* *     * *
* ******* *
*         *
***********

Gracias a la pereza de Haskell, esto se ejecuta en la memoria constante. Se utiliza el enfoque obvio, es decir, un bucle sobre yy xy elegir entre *y , en función de

  • si la posición actual está encima o debajo de una diagonal
  • xresp. yes par o impar
  • n/2 es par o impar
nimi
fuente
2

Lisp común - 346

(lambda(n &aux(d 0))(tagbody $ #6=(#7=dotimes(i n)#4=(princ"*"))#2=(#7#(i d)#5=(princ" ")#4#)#3=(terpri)#1=(#7#(i d)#4##5#)(when(> n 0)(#7#(i(1- n))#5#)#4#)#2##3#(when(> n 3)#1##4##4#(incf d)(decf n 4)(go $))(go /)@(decf d)(incf n 4)(when(> n 3)#2##5##4##3#)/ #1#(when(> n 0)#4#)(when(> n 1)(#7#(i(- n 2))#5#)#4#)#2##3##1##6#(when(> d 0)(go @))))

Solución iterativa con uso constante de memoria. Lo anterior hace uso de pesados #n=y #n#las variables de lectores. A pesar de que hay enfoques más directos, aquí comencé con una función recursiva y la modifiqué para simular la recursividad con gotodeclaraciones: esto probablemente no se pueda leer.

Salida para todos los valores de entrada de 0 a 59 .

Versión recursiva original, con información de depuración.

(nota: terprisignifica newline)

(defun spiral (n &optional (d 0) )
  (flet ((prefix ()
           (format t "~4d~4d | " n d)
           (dotimes (i d)
             (princ "a ")))
         (postfix ()
           (dotimes (i d)
             (princ " b"))))
    (when (= d 0) (prefix))
    (dotimes (i n) (princ "c"))
    (postfix)
    (terpri)

    (prefix)
    (when (> n 0)
      (dotimes (i (1- n)) (princ " "))
      (princ "d"))
    (postfix)
    (terpri)

    (when (> n 3)
      (prefix)
      (princ "**")
      (spiral (- n 4) (1+ d))
      (postfix)
      (princ " f")
      (terpri))

    (prefix)
    (when (> n 0)
      (princ "g"))

    (when (> n 1)
      (dotimes (i (- n 2)) (princ " "))
      (princ "h"))
    (postfix)
    (terpri)

    (prefix)
    (dotimes (i n) (princ "i"))
    ))

Por ejemplo:

(spiral 8)

   8   0 | cccccccc
   8   0 |        d
   8   0 | **cccc b
   4   1 | a    d b
   4   1 | a ** b b
   0   2 | a a  b b
   0   2 | a a  b b
   0   2 | a a  b f
   4   1 | a g  h b
   4   1 | a iiii f
   8   0 | g      h
   8   0 | iiiiiiii

Vea también esta pasta con todos los resultados del 0 al 59 (no es lo mismo que el anterior, este es más detallado).

Versión iterativa, con información de depuración.

(defun spiral (n &aux (d 0) )
  (flet ((prefix ()
           (format t "~4d~4d | " n d)
           (dotimes (i d)
             (princ "a ")))
         (postfix ()
           (dotimes (i d)
             (princ " b"))))
    (tagbody
     step-in
       (when (= d 0) (prefix))
       (dotimes (i n) (princ "c"))
       (postfix)
       (terpri)

       (prefix)
       (when (> n 0)
         (dotimes (i (1- n)) (princ " "))
         (princ "d"))
       (postfix)
       (terpri)

       (when (> n 3)
         (prefix)
         (princ "**")

         (incf d)
         (decf n 4)
         (go step-in))

       (go skip)

     step-out
       (decf d)
       (incf n 4)
       (when (> n 3)
         (postfix)
         (princ " f")
         (terpri))

     skip
       (prefix)
       (when (> n 0)
         (princ "g"))

       (when (> n 1)
         (dotimes (i (- n 2)) (princ " "))
         (princ "h"))
       (postfix)
       (terpri)

       (prefix)
       (dotimes (i n) (princ "i"))
       (when(> d 0)(go step-out)))))
volcado de memoria
fuente
¿Puedes explicar cómo esto cumple con la restricción de memoria? Solo veo un punto de recursión, que es bueno, pero ¿puedes entrar en un poco más de detalle?
durron597
@ durron597 Sí, estoy trabajando en esto. Esto es actualmente O (n) porque llamamos a la función recursivamente un número de tiempo proporcional a ny la pila de llamadas crece en consecuencia, pero en este caso, podemos simular la recursión con dos bucles: uno con ndisminución y daumento (hasta n <= 3 ), y otro con ddisminución a cero. No tengo mucho tiempo para trabajar en esto en este momento, pero intentaré actualizar la respuesta en consecuencia. Por cierto, hay formas más directas de imprimir la espiral, pero fue divertido tratar de definirla de forma recursiva.
coredump
2

CJam, 72 bytes

li_2/:M;)__*{1$mdM-\M-_2$)=2$0<*@_*@_0>-_*e>mQ_M>2*@@+M+2%+'#S+N+N+=o}/;

Esta es una conversión bastante directa de mi solución C a CJam. No es tan corto como normalmente esperaría de una solución CJam, pero esta realmente sufre de la restricción de memoria. Los beneficios comunes de acumular resultados en la pila que se vuelca automáticamente al final, y usar operaciones sofisticadas de lista / cadena, todo se va por la ventana. Esto genera y genera la solución de un carácter a la vez. La pila solo contiene unos pocos enteros en tiempo de ejecución y está vacía al final.

Aunque no es una gran muestra del uso de un lenguaje de golf, sigue siendo considerablemente más corto que el código C solo porque la notación es más compacta.

Explicación:

li    Get input n.
_2/   Calculate n/2.
:M;   Store it in variable M
)__*  Calculate (n+1)*(n+1), which is the total number of output characters.
      Also keep a copy of n+1 on the stack.
{     Start loop over output character positions.
  1$md  Calculate divmod of position with n+1. This gives y and x of position.
  M-    Subtract M from x.
  \M-   Subtract M from y.
  _     Copy y.
  2$)   Calculate x+1.
  =     Check if y == x+1
  2$0<  Check if x < 0.
  *     Multiply the two check results. This is the result of the flip
        condition for the top-left diagonal to turn the rectangles into a spiral.
  @_*   Calculate x*x.
  @_    Get y to top of stack, and copy it.
  0>-   Subtract 1 from y if it is in the bottom half.
  _*    Calculate y*y.
  e>    Take maximum of x*x and y*y...
  mQ    ... and calculate the square root. This is the absolute value of the
        larger of the two.
  _M>   Check if the value is greater M, which means that this is the
        position of a line end.
  2*    Multiply by 2 so that we can add another condition to it later.
  @     Get result of diagonal flip condition to the stack top.
  @     Get max(x,y) to the top.
  +M+   Add the two, and add M to the whole thing. This value being even/odd
        determines if the output is a # or a space.
  2%    Check if value is odd.
  +     Add to line end condition to get a single ternary condition result.
  '#S+N+N+
        Build string "# \n\n".
  =     Use the condition result to pick the output character out of the string.
  o     Output the character.
}/    End loop over output characters.
;     Pop n+1 value off stack, to leave it empty.
Reto Koradi
fuente