Stampa una spirale ascii nella memoria O (log n)


13

Puoi scrivere un programma o una funzione che riceve un numero intero dispari, positivo n , dove n >= 3, come argomento di funzione, argomenti della riga di comando o su STDIN (o equivalente per il tuo sistema), e stampa su STDOUT (o equivalente di sistema) una spirale ASCII che ruota verso l'interno in senso orario dove il bordo superiore è esattamente nlungo i caratteri. Il primo bordo destro dovrebbe essere n+1lungo i personaggi, ovviamente. Per esempio,

Ingresso:

11

Produzione:

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

Le catture:

  • Il tuo programma non deve usare altro che O(log n)memoria .
  • Il programma può stampare solo i caratteri *(ASCII 42), (ASCII 32), <CR>(ASCII 13) e <LF>(ASCII 10).
  • Il programma deve stampare la stringa, non restituirla dalla funzione.
  • La restrizione Big-O è solo sulla memoria , non ci sono nessun restrizioni sulla fase di esecuzione .
  • Una nuova riga finale è facoltativa.
  • Se la tua lingua non supporta tipi di interi di grandi dimensioni, non è necessario supportare un livello superiore a quello che supporta, ma potresti non utilizzare questo come un trucco per dire "oh, beh, non devo supportare sopra X quindi puoi semplicemente fare di un enorme array la dimensione massima ogni volta "

Le scappatoie standard sono vietate, come al solito.


2
Non credo sia possibile. Non è possibile memorizzare l'ingresso nnella memoria O (1).
xnor

@xnor "O (1) costituisce un uso costante della memoria. Quindi la quantità di input è irrilevante" - Se l'input n si adatta a un numero intero, allora sono sicuro che può essere codificato in un uso di memoria costante.
André,

1
La memorizzazione dell'ingresso nrichiede log nbit. Man mano che nsi ingrandisce, aumenta anche lo spazio necessario per memorizzarlo. Stai forse dicendo di farlo con un numero limitato di variabili?
xnor

Oppure, in alternativa, esiste un limite n?
Sp3000,

Penso che stia dicendo che non è possibile memorizzare l'intero output in una volta, quindi stamparlo tutto in una volta perché diventerà più grande. Probabilmente devi stamparlo in modo ricorsivo.
Jacob

Risposte:


9

C, 125 121 byte

Versione golfata Non ha variabili k. La variabile kviene utilizzata nella versione non golfata solo per facilitare la leggibilità. Anche i forcondizionali ad anello sono riorganizzati e una serie di inutili {}rimossi. Un altro set di {}può essere rimosso migrando puts("")all'interno delle parentesi del jloop nella posizione di inizializzazione, ma ciò significherebbe una nuova riga all'inizio dell'output, quindi non l'ho fatto.

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("");}}}

Stampa nun'ampia a n+1spirale alta come nell'esempio.

Spiegazione

Fondamentalmente ho dimezzare il valore di n(arrotondamento per difetto) ed eseguire due cicli: uno esterno ida -n/2-1per n/2+1stampare le righe ( i=0viene soppresso così otteniamo n+1righe) ed uno interno jda ( -n/2a n/2Usiamo per stampare i caratteri.) expression & 1Per stampare strisce e la condizione j*j<i*iper decidere se stampare strisce verticali o orizzontali (verticale ai lati in cui la magnitudine assoluta iè maggiore e orizzontale in alto e in basso.) È necessaria una regolazione +nper aiutare con la terminazione corretta a seconda che n/2sia dispari o anche.

kè normalmente 1 e fornisce una regolazione per il fatto che i valori assoluti di iintervallo da 1 a n/2+1mentre i valori assoluti di jintervallo da 0 a n/2. Se kfosse sempre 1 otterremmo rettangoli concentrici, ma viene invertito a 0 quando viene invertita i==j&i<=0una fila diagonale di celle, producendo la spirale.

non golfato nel programma di test

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);
}

Produzione

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

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

3
***
  *
* *
***

1
*
*

Battimi un po '... +1 questo è pazzesco!
sudo rm -rf slash


7

C, 118 byte

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));}

Codice prima del golf finale:

#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;
}

L'osservazione chiave è che il modello è quasi una serie di quadrati concentrici. Con un paio di lievi rughe:

  • La dimensione y è una più grande della dimensione x. Ciò viene corretto sottraendo 1 da y per la metà inferiore, che essenzialmente ripete la riga centrale.
  • Per trasformare i rettangoli in una spirale, i pixel lungo la y = x + 1diagonale devono essere invertiti fino al centro della forma.

Per il resto, il codice scorre semplicemente su tutte le posizioni, calcolando la distanza di Chebyshev dal centro per ogni posizione ed emettendo uno dei due caratteri a seconda della distanza pari o dispari. Ed emettendo una nuova riga per l'ultima posizione di ogni riga.

Poiché ci sono solo poche variabili scalari e i caratteri vengono emessi uno per uno, l'utilizzo della memoria è ovviamente costante.


Risposta eccellente, ma poiché non si inizializza, ppenso che si verifichino fallo di meta.codegolf.stackexchange.com/q/4939/15599 . Inoltre, non sono sicuro di dichiarare le variabili globali durante l'invio di una funzione. Ovviamente la mia risposta sarebbe più corta di 4 byte se lo facessi. Ho iniziato un meta post meta.codegolf.stackexchange.com/q/5532/15599
Level River St

Sì, mi è venuto in mente che probabilmente avrei dovuto inizializzare p.
Reto Koradi

3

C ++, 926 byte

#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;}

Questo non è elegante, ma non occupa molta memoria per n grandi. Inoltre, ci sono (quasi certamente) circa 20 personaggi che possono essere ulteriormente giocati a golf, ma non sopporto più di guardarli.

Breve spiegazione:

Questo divide le linee nelle spirali in due tipi: quelli con ****** nel mezzo e quelli con \ s \ s \ s \ s \ s nel mezzo. Quindi è chiaro che ogni riga è composta da diversi "*" s, il centro e alcuni "*". Capire esattamente quante di ogni cosa è semplice se si guarda il modello abbastanza a lungo. La cosa difficile è stata stampare il centro della spirale, che praticamente ho programmato con un codice usando un condizionale. Questo è risultato utile perché le linee *** e \ s \ s \ s cambiano essendo dispari / anche lì.

test:

Ingresso: 55 (penso che quelli grandi siano più belli)

Produzione:

************************************************** *****
                                                      *
************************************************** *** *
* * *
* *************************************************** * *
* * * * *
* * ********************************************* * * *
* * * * * * *
* * * ***************************************** * * * *
* * * * * * * * *
* * * * ************************************* * * * * *
* * * * * * * * * * *
* * * * * ********************************* * * * * * *
* * * * * * * * * * * * * *
* * * * * * ***************************** * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * ************************* * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * * * ********************* * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * ***************** * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ************* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * ********* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ***** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * {- il mio programma aggiunge uno spazio qui tra
* * * * * * * * * * * * * *** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ******* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * *********** * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * *************** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * ******************* * * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * * *********************** * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * *************************** * * * * * * *
* * * * * * * * * * * * * *
* * * * * * ******************************* * * * * * *
* * * * * * * * * * * * *
* * * * * *********************************** * * * * *
* * * * * * * * * *
* * * * *************************************** * * * *
* * * * * * * *
* * * ********************************************* * * *
* * * * * *
* * ************************************************* * *
* * * *
* *************************************************** ** *
* *
************************************************** *****

Ingresso: 3

Produzione:

***
  *
* * 
***

Nota: non sono uno scienziato informatico / studente CS e non so come dimostrare che questo utilizza la memoria O (log n). Posso solo capire cosa fare in base ai collegamenti nella domanda. Le sarei grato se qualcuno potesse confermare / negare se questa risposta è valida. La mia logica per la validità di questa risposta è che non memorizza mai alcuna variabile di dimensione basata su n tranne l'input stesso. Invece, un ciclo for che esegue n volte calcola i valori interi in base a n. Esiste lo stesso numero di questi valori indipendentemente dall'input.

Nota2: Questo non funziona per n = 1 a causa del mio metodo di gestione del mezzo. Questo sarebbe facile da risolvere con i condizionali, quindi se qualcuno è entro pochi caratteri dalla mia risposta, lo risolverò;)

Gioca con esso su ideone.


Credo che sia valido, anche se è necessario leggere questo codice C ++ su una sola riga. ;) La tua comprensione è corretta. Non è possibile utilizzare memoria con dimensioni che dipendono da n. Un esempio tipico che non soddisfa il requisito sarebbe un tipo di stringa / buffer / matrice che contiene una linea completa di output.
Reto Koradi,

Poiché questa è l'unica risposta, ho adattato la domanda in modo da non richiedere manipolazione n=1, poiché non considero interessante un involucro così speciale.
durron597,

3

Haskell, 151 byte

(#)=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)

Esempio di utilizzo:

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

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

Grazie alla pigrizia di Haskell ciò scorre nella memoria costante. Esso utilizza il metodo ovvio, vale a dire un ciclo su ye xe scegliere tra *e , a seconda della

  • se la posizione corrente è sopra o sotto una diagonale
  • xresp. yè pari o dispari
  • n/2 è pari o dispari

2

Lisp comune - 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 @))))

Soluzione iterativa con utilizzo costante della memoria. Quanto sopra rende usi pesanti #n=e #n#variabili lettore. Anche se ci sono approcci più diretti, qui ho iniziato con una funzione ricorsiva e l'ho modificata per simulare la ricorsione con gotoaffermazioni: questo è probabilmente illeggibile.

Uscita per tutti i valori di input da 0 a 59 .

Versione ricorsiva originale, con informazioni di debug

(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"))
    ))

Per esempio:

(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

Vedi anche questo incolla con tutti i risultati da 0 a 59 (non uguale a quello sopra, questo è più dettagliato).

Versione iterativa, con informazioni di debug

(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)))))

Puoi spiegare come questo soddisfa la restrizione della memoria? Vedo solo un punto di ricorsione, il che è positivo, ma puoi approfondire un po 'di più?
durron597,

@ durron597 Sì, ci sto lavorando. Questo è attualmente O (n) perché chiamiamo la funzione ricorsivamente un numero di volte proporzionale a ne lo stack di chiamate cresce di conseguenza, ma in questo caso, possiamo simulare la ricorsione con due loop: uno con ndecrescente e dcrescente (fino a n <= 3 ) e un altro con driduzione a zero. Non ho molto tempo per lavorare su questo in questo momento, ma proverò ad aggiornare la risposta di conseguenza. A proposito, ci sono modi più diretti per stampare la spirale, ma è stato divertente provare a definirla in modo ricorsivo.
coredump,

2

CJam, 72 byte

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

Questa è una conversione abbastanza diretta della mia soluzione C in CJam. Non così breve come ci si aspetterebbe da una soluzione CJam, ma questa soffre davvero della restrizione della memoria. I vantaggi comuni della creazione di risultati sullo stack che viene scaricato automaticamente alla fine e dell'utilizzo di operazioni di lista / stringa elaborate, vanno tutti fuori dalla finestra. Questo genera e genera la soluzione un carattere alla volta. Lo stack contiene solo pochi numeri interi in fase di runtime ed è vuoto alla fine.

Anche se non è un ottimo display dell'uso di un linguaggio da golf, è comunque notevolmente più breve del codice C solo perché la notazione è più compatta.

Spiegazione:

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.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.