Cetak spiral ascii dalam memori O (log n)

13

Anda dapat menulis sebuah program atau fungsi yang menerima bilangan bulat ganjil, positif n , di mana n >= 3, baik sebagai argumen fungsi, argumen baris perintah, atau pada STDIN (atau setara dengan sistem Anda), dan mencetak ke STDOUT (atau setara sistem) spiral ASCII yang berputar ke dalam searah jarum jam di mana tepi atas persis npanjang karakter. Tepi kanan pertama harus n+1panjang karakter, jelas. Contohnya,

Memasukkan:

11

Keluaran:

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

Tangkapan:

  • Program Anda harus menggunakan tidak lebih dari O(log n)memori .
  • Program Anda hanya dapat mencetak karakter *(ASCII 42), (ASCII 32), <CR>(ASCII 13) dan <LF>(ASCII 10).
  • Program Anda harus mencetak string, bukan mengembalikannya dari fungsi.
  • Pembatasan Big-O hanya pada memori , tidak ada batasan pada runtime .
  • Baris baru tambahan adalah opsional.
  • Jika bahasa Anda tidak mendukung tipe integer besar, Anda tidak harus mendukung lebih tinggi dari apa yang didukungnya, tetapi Anda mungkin tidak menggunakan ini sebagai trik untuk mengatakan "oh, well, saya tidak harus mendukung di atas X jadi saya hanya dapat membuat array besar ukuran maksimum setiap kali "

Celah standar dilarang, seperti biasa.

durron597
sumber
2
Saya tidak percaya ini mungkin. Seseorang tidak dapat menyimpan input ndalam memori O (1).
xnor
@ xnor "O (1) merupakan penggunaan memori yang konstan. Jadi jumlah input tidak penting" - Jika input n cocok dengan integer, maka saya yakin itu dapat dikodekan menjadi penggunaan memori yang konstan.
André
1
Menyimpan input nmembutuhkan log nbit. Semakin nbesar, demikian pula ruang yang dibutuhkan untuk menyimpannya. Apakah Anda mungkin mengatakan untuk melakukan ini dengan sejumlah variabel?
xnor
Atau, apakah ada batasan n?
Sp3000
Saya pikir dia mengatakan Anda tidak dapat menyimpan seluruh output sekaligus, lalu cetak semuanya sekaligus karena itu akan menjadi lebih besar. Anda mungkin harus mencetaknya secara rekursif.
Jacob

Jawaban:

9

C, 125 121 byte

Versi golf Ini tidak memiliki variabel k. Variabel kdigunakan dalam versi yang tidak diklik hanya untuk membantu keterbacaan. Juga forpersyaratan loop diatur ulang dan satu set yang tidak perlu {}dihapus. Set lain {}dapat dihilangkan dengan bermigrasi puts("")di dalam kurung jloop di posisi inisialisasi, tetapi ini berarti baris baru di awal output, jadi saya belum melakukannya.

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

Mencetak nlebar dengan n+1spiral tinggi seperti contohnya.

Penjelasan

Pada dasarnya saya membagi dua nilai n(pembulatan ke bawah) dan menjalankan dua loop: satu luar idari -n/2-1ke n/2+1untuk mencetak baris ( i=0ditekan sehingga kita mendapatkan n+1baris) dan satu bagian dalam jdari ( -n/2ke n/2Kami menggunakan untuk mencetak karakter.) expression & 1Untuk mencetak garis-garis , dan kondisi j*j<i*iuntuk memutuskan apakah akan mencetak garis-garis vertikal atau horizontal (vertikal di sisi-sisi di mana besaran absolut ilebih besar, dan horizontal di atas dan bawah.) Penyesuaian +ndiperlukan untuk membantu penghentian yang benar tergantung pada apakah n/2aneh atau tidak. bahkan.

kbiasanya 1, dan memberikan penyesuaian untuk fakta bahwa nilai absolut irentang dari 1 hingga n/2+1nilai absolut jrentang dari 0 hingga n/2. Jika kselalu 1 kita akan mendapatkan persegi panjang konsentris, tetapi itu terbalik ke 0 ketika i==j&i<=0sehingga baris diagonal sel terbalik, menghasilkan spiral.

ungolfed dalam program tes

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

Keluaran

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

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

3
***
  *
* *
***

1
*
*
Level River St
sumber
Kalahkan saya sedikit ... +1 ini gila pendek!
sudo rm -rf slash
1
114 bytes
ceilingcat
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));}

Kode sebelum golf terakhir:

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

Pengamatan kuncinya adalah polanya hampir merupakan serangkaian kotak konsentris. Dengan sedikit kerutan:

  • Ukuran y lebih besar dari ukuran x. Ini dikoreksi dengan mengurangi 1 dari y untuk bagian bawah, yang pada dasarnya mengulangi baris tengah.
  • Untuk mengubah persegi panjang menjadi spiral, piksel sepanjang y = x + 1 diagonal perlu dibalik hingga ke tengah bentuk.

Untuk selebihnya, kode ini hanya mengulang semua posisi, menghitung jarak Chebyshev dari pusat untuk setiap posisi, dan memancarkan satu dari dua karakter tergantung pada jarak genap atau ganjil. Dan memancarkan baris baru untuk posisi terakhir setiap baris.

Karena hanya ada beberapa variabel skalar, dan karakter dipancarkan satu per satu, penggunaan memori jelas konstan.

Reto Koradi
sumber
Jawaban yang sangat bagus, tetapi karena Anda tidak menginisialisasi, psaya pikir Anda salah meta.codegolf.stackexchange.com/q/4939/15599 . Saya juga tidak yakin tentang mendeklarasikan variabel global saat mengirimkan fungsi. Jelas jawaban saya akan lebih pendek 4 byte jika saya melakukan ini. Saya sudah memulai posting meta meta.codegolf.stackexchange.com/q/5532/15599
Level River St
Ya, terlintas dalam pikiran saya bahwa saya mungkin harus menginisialisasi 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;}

Ini tidak elegan, tetapi tidak memakan banyak memori untuk n besar. Lebih jauh lagi, ada (hampir pasti) sekitar 20 karakter yang dapat di-pegolf lebih jauh, tetapi saya tidak tahan melihatnya lagi.

Penjelasan Singkat:

Ini membagi garis dalam spiral menjadi dua jenis: yang dengan ****** di tengah, dan yang dengan \ s \ s \ s \ s \ s \ s di tengah. Maka jelas bahwa setiap baris terdiri dari beberapa "*" s, tengah, dan beberapa "*". Mencari tahu persis berapa banyak dari setiap hal itu sederhana jika Anda melihat polanya cukup lama. Yang sulit adalah mencetak pusat spiral yang saya kode pada dasarnya menggunakan kondisional. Ini akhirnya menjadi berguna karena saluran *** dan switch berubah menjadi ganjil / bahkan di sana.

Tes:

Input: 55 (Saya pikir yang besar terlihat paling keren)

Keluaran:

************************************************ *****
                                                      *
************************************************ *** *
* * *
* ************************************************* * *
* * * * *
* * ******************************************* * * *
* * * * * * *
* * * *************************************** * * * *
* * * * * * * * *
* * * * ************************************ * * * * *
* * * * * * * * * * *
* * * * * ******************************* * * * * * *
* * * * * * * * * * * * *
* * * * * * **************************** * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * ************************* * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * * * ********************* * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * * * ***************** * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ************* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * ********* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ***** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * {- program saya menambahkan spasi di sini btw
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ******* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * *********** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * *************** * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * ******************* * * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * * *********************** * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * *************************** * * * * * * *
* * * * * * * * * * * * * *
* * * * * * ***************************** * * * * * *
* * * * * * * * * * * *
* * * * * ********************************* * * * * *
* * * * * * * * * *
* * * * ************************************** * * * *
* * * * * * * *
* * * ***************************************** * * *
* * * * * *
* * ********************************************* * *
* * * *
* ************************************************* ** *
* *
************************************************ *****

Memasukkan: 3

Keluaran:

***
  *
* * 
***

Catatan: Saya bukan seorang ilmuwan komputer / mahasiswa CS, dan saya tidak tahu bagaimana membuktikan bahwa ini menggunakan memori O (log n). Saya hanya bisa menentukan apa yang harus dilakukan berdasarkan tautan dalam pertanyaan. Saya akan berterima kasih jika seseorang dapat mengkonfirmasi / menolak jika jawaban ini valid. Logika saya untuk validitas jawaban ini adalah bahwa ia tidak pernah menyimpan variabel ukuran berdasarkan n kecuali input itu sendiri. Sebagai gantinya, untuk loop yang berjalan n kali menghitung nilai integer berdasarkan pada n. Ada jumlah yang sama dari nilai-nilai itu terlepas dari input.

Note2: Ini tidak berfungsi untuk n = 1 karena metode saya berurusan dengan tengah. Ini akan mudah untuk diperbaiki dengan persyaratan, jadi jika ada yang berada dalam beberapa karakter jawaban saya, saya akan memperbaikinya;)

Bermain dengannya di ideone.

sudo rm -rf slash
sumber
Saya percaya ini valid, meskipun banyak kode C ++ pada satu baris ini harus dibaca. ;) Pemahaman Anda benar. Anda tidak dapat menggunakan memori apa pun dengan ukuran yang tergantung n. Contoh khas yang tidak memenuhi persyaratan adalah beberapa jenis string / buffer / array yang menampung garis keluaran lengkap.
Reto Koradi
Karena ini adalah satu-satunya jawaban, saya telah menyesuaikan pertanyaan untuk tidak memerlukan penanganan n=1, karena saya tidak menganggap casing khusus menarik.
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)

Contoh penggunaan:

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

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

Berkat kemalasan Haskell, ini berjalan dalam memori konstan. Ini menggunakan pendekatan yang jelas, yaitu perulangan ydan xdan memilih antara *dan , tergantung pada

  • jika posisi saat ini di atas atau di bawah diagonal
  • xresp. ygenap atau ganjil
  • n/2 genap atau ganjil
nimi
sumber
2

Gangguan Umum - 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 @))))

Solusi berulang dengan penggunaan memori yang konstan. Hal-hal di atas banyak menggunakan variabel reader #n=dan #n#. Meskipun ada pendekatan yang lebih langsung, di sini saya mulai dengan fungsi rekursif dan memodifikasinya untuk mensimulasikan rekursi dengan gotopernyataan: ini mungkin tidak dapat dibaca.

Output untuk semua nilai input dari 0 hingga 59 .

Versi rekursif asli, dengan informasi debug

(catatan: terpriberarti 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"))
    ))

Sebagai contoh:

(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

Lihat juga tempel ini dengan semua hasil dari 0 hingga 59 (tidak sama dengan di atas, yang ini lebih verbose).

Versi berulang, dengan informasi 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)))))
coredump
sumber
Bisakah Anda menjelaskan bagaimana ini memenuhi batasan memori? Saya hanya melihat satu titik rekursi, yang bagus, tetapi bisakah Anda menjelaskan lebih detail?
durron597
@ durron597 Ya, saya sedang mengerjakan ini. Ini saat ini O (n) karena kita memanggil fungsi secara rekursif sejumlah waktu yang sebanding dengan ndan tumpukan panggilan tumbuh sesuai, tetapi dalam kasus ini, kita dapat mensimulasikan rekursi dengan dua loop: satu dengan npenurunan dan dpeningkatan (sampai n <= 3 ), dan satu lagi dengan dmenurun ke nol. Saya tidak punya banyak waktu untuk mengerjakan ini sekarang, tetapi saya akan mencoba memperbarui jawabannya. Btw, ada cara yang lebih langsung untuk mencetak spiral, tetapi menyenangkan mencoba mendefinisikannya secara rekursif.
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}/;

Ini adalah konversi solusi C saya yang cukup langsung ke CJam. Tidak sesingkat yang biasanya Anda harapkan dari solusi CJam, tetapi yang ini benar-benar menderita dari pembatasan memori. Manfaat umum dari membangun hasil pada stack yang akan dibuang secara otomatis di akhir, dan menggunakan operasi daftar / string yang mewah, semua keluar jendela. Ini menghasilkan dan mengeluarkan solusi satu karakter pada satu waktu. Tumpukan hanya berisi beberapa bilangan bulat saat runtime, dan kosong di akhir.

Meskipun itu bukan tampilan yang bagus menggunakan bahasa golf, itu masih jauh lebih pendek daripada kode C hanya karena notasi lebih kompak.

Penjelasan:

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
sumber