O (log n) belleğe bir ascii spirali yazdırın


13

Bir alan bir program veya fonksiyon yazabilirsiniz garip, pozitif bir tamsayı n , nerede n >= 3, STDOUT (veya sistem eşdeğeri) bir ASCII spiral ya fonksiyon parametresi, komut satırı argümanları veya STDIN (veya sistem için eşdeğer) ve baskılar olarak üst kenarın tam olarak nkarakter olduğu yerde saat yönünde döner . İlk sağ kenar n+1açıkça karakter uzunluğunda olmalıdır . Örneğin,

Giriş:

11

Çıktı:

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

Yakalar:

  • Programınız O(log n)bellekten fazlasını kullanmamalıdır .
  • Programınız yalnızca *(ASCII 42), (ASCII 32), <CR>(ASCII 13) ve <LF>(ASCII 10) karakterlerini yazdırabilir .
  • Programınız dizeden yazdırmalı, işlevden döndürmemelidir.
  • Big-O kısıtlaması sadece bellekte , çalışma zamanında herhangi bir kısıtlama yok .
  • Sondaki bir satırsonu isteğe bağlıdır.
  • Diliniz büyük tamsayı türlerini desteklemiyorsa, desteklediğinden daha yüksek bir desteği desteklemenize gerek yoktur, ancak bunu "oh, iyi, X'in üstünde desteklememe gerek yok" demek için bir hile olarak kullanamayabilirsiniz. her seferinde büyük bir diziyi maksimum boyut haline getirebilir "

Standart boşluklar her zamanki gibi yasaklanmıştır.


2
Bunun mümkün olduğuna inanmıyorum. Giriş nO (1) belleğe kaydedilemez.
xnor

@xnor "O (1) sabit bir bellek kullanımı oluşturur. Bu nedenle girdi miktarı önemsizdir" - n girdisi tamsayıya uyuyorsa, sabit bellek kullanımına kodlanabileceğinden eminim.
André

1
Girişin kaydedilmesi bit nalır log n. Gibi nbüyüdükçe, bu yüzden saklamak için gerekli alanı yok. Belki de bunu sınırlı sayıda değişkenle yapmayı mı söylüyorsunuz?
xnor

Veya alternatif olarak, üzerinde bir sınır var nmı?
Sp3000

Sanırım tüm çıktıyı bir kerede saklayamayacağınızı söylüyor, sonra hepsini bir kerede yazdırın çünkü bu daha da büyüyecek. Muhtemelen yinelemeli olarak yazdırmanız gerekir.
Jacob

Yanıtlar:


9

Cı, 125 121 bayt

Golfçü versiyon Bu bir değişkene sahip değildir k. Değişken k, okunabilirliğe yardımcı olmak için çözülmemiş versiyonda kullanılır. Ayrıca fordöngü koşulları yeniden düzenlenir ve bir set gereksiz {}kaldırılır. Başlatma konumunda, döngünün köşeli parantezinin içine {}geçirilerek başka bir dizi seti çıkarılabilir , ancak bu, çıktının başlangıcında bir yeni satır anlamına gelir, bu yüzden yapmadım.puts("")j

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

Örnek gibi ngeniş bir n+1yüksek spiral ile yazdırır .

açıklama

Temelde ben değerini yarıya n(aşağı yuvarlama) ve iki döngüler çalıştırın: bir dış tane igelen -n/2-1etmek n/2+1satırları yazdırmak için ( i=0biz almak böylece bastırılır n+1satırları) ve bir iç tane jden ( -n/2hiç n/2Biz kullanmak karakterleri yazdırmak için.) expression & 1Şeritler yazdırmak için ve j*j<i*idikey veya yatay şeritlerin (mutlak büyüklüğün idaha büyük olduğu kenarlarda dikey , üstte ve altta yatay) yazdırılıp yazdırılmayacağına karar verme koşulu , tek veya düzensiz +nolmasına bağlı olarak doğru sonlandırmaya yardımcı olması için bir ayarlama yapılması gerekir. n/2hatta.

knormalde 1'dir ve mutlak değerlerin i1'den 1'e n/2+1, mutlak değerlerin j0'dan 0'a kadar olması için bir ayar sağlar n/2. Her kzaman 1 olsaydı, eşmerkezli dikdörtgenler elde ederdik, ancak i==j&i<=0çapraz bir hücre sırası ters çevrildiğinde, spiral üreterek 0'a çevrilir.

test programında çözülmemiş

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

Çıktı

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

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

3
***
  *
* *
***

1
*
*

Beni biraz döv ... +1 bu çok kısa!
sudo rm -rf slash


7

C, 118 bayt

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

Son golf öncesi kod:

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

Temel gözlem, desenin neredeyse bir dizi eş merkezli kare olmasıdır. Birkaç hafif kırışıklık ile:

  • Y boyutu, x boyutundan bir büyüktür. Bu, esas olarak orta sırayı tekrarlayan alt yarı için 1'den y çıkarılmasıyla düzeltilir.
  • Dikdörtgenleri bir spirale dönüştürmek için, y = x + 1diyagonal boyunca piksellerin şeklin ortasına kadar ters çevrilmesi gerekir.

Geri kalanı için, kod tüm konumlar üzerinde döngü yapıyor, her konum için merkezden Chebyshev mesafesini hesaplıyor ve mesafeye eşit veya tek olan iki karakterden birini yayıyor. Ve her satırın son konumu için yeni bir satır yayar.

Sadece birkaç skaler değişken olduğundan ve karakterler tek tek yayınlandığından, bellek kullanımı açıkça sabittir.


Mükemmel cevap, ama başlatmazsanız meta.codegolf.stackexchange.com/q/4939/15599p faul yaptığınızı düşünüyorum . Ayrıca bir işlev gönderirken genel değişkenleri bildirmekten de emin değilim. Eğer bunu yaparsam cevabım 4 byte daha kısa olurdu. Bir meta sonrası başladım meta.codegolf.stackexchange.com/q/5532/15599
Seviye Nehri St

Evet, muhtemelen başlatmam gerektiğini düşündüm p.
Reto Koradi

3

C ++, 926 bayt

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

Bu zarif değil, ama büyük n için fazla bellek gerektirmiyor. Dahası, daha fazla golf oynayabilen (neredeyse kesinlikle) yaklaşık 20 karakter var, ama artık ona bakamıyorum.

Kısa Açıklama:

Bu, spirallerdeki çizgileri iki türe böler: ortada ******, ortada \ s \ s \ s \ s \ s olanlar. Daha sonra her satırın birkaç "*", orta ve bir "*" işaretinden oluştuğu açıktır. Eğer desene yeterince uzun süre bakarsanız, her şeyin kaçının basit olduğunu anlamak. Zor olan şey, temel olarak bir koşullu kullanarak kodladığım spiralin merkezini basmaktı. Bu yararlı oldu çünkü *** ve \ s \ s \ s hatları garip / hatta orada değişir.

Testler:

Girdi: 55 (Bence büyük olanlar havalı görünüyor)

Çıktı:

************************************************** *****
                                                      *
************************************************** *** *
* * *
* ************************************************* * *
* * * * *
* * ********************************************* * * *
* * * * * * *
* * * ***************************************** * * * *
* * * * * * * * *
* * * * ************************************* * * * * *
* * * * * * * * * * *
* * * * * ********************************* * * * * * *
* * * * * * * * * * * * *
* * * * * * ***************************** * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * ************************* * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * ********************* * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * ***************** * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ************* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * ********* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * ***** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * {- programım buraya bir boşluk ekler btw
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ******* * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * *********** * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * *************** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * ******************* * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * *********************** * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * *************************** * * * * * * *
* * * * * * * * * * * * * *
* * * * * * ******************************* * * * * * *
* * * * * * * * * * * *
* * * * * *********************************** * * * * *
* * * * * * * * * *
* * * * *************************************** * * * *
* * * * * * * *
* * * ******************************************* * * *
* * * * * *
* * *********************************************** * *
* * * *
* ************************************************* ** *
* *
************************************************** *****

Giriş: 3

Çıktı:

***
  *
* * 
***

Not: Bilgisayar bilimcisi / CS öğrencisi değilim ve bunun O (log n) belleği kullandığını nasıl kanıtlayacağımı bilmiyorum. Sadece sorudaki bağlantılara dayanarak ne yapacağımı öğrenebilirim. Birisi bu cevabın geçerli olup olmadığını onaylayabilir / reddedebilirse minnettar olurum. Bu yanıtın geçerliliği için mantığım, girişin kendisi dışında n'ye dayalı hiçbir boyut değişkeni asla saklamamasıdır. Bunun yerine, n kez çalışan bir for döngüsü n'ye dayalı tamsayı değerleri hesaplar. Girdi ne olursa olsun bu değerlerin aynı sayısı vardır.

Not2: Orta ile baş etme yöntemim nedeniyle bu n = 1 için işe yaramıyor. Bu, koşullarla düzeltilmesi kolay olurdu, bu yüzden herhangi bir kişi cevabımın birkaç karakterinden biri varsa, düzelteceğim;)

Onunla ideone üzerinde oyna.


Tek bir satırda bu kadar C ++ kodu tür okumak zorunda olsa bile, geçerli olduğuna inanıyorum. ;) Anlayışınız doğru. Büyüklüğüne bağlı herhangi bir belleği kullanamazsınız n. Gereksinimi karşılamayan tipik bir örnek, tam bir çıktı satırı tutan bir tür dize / tampon / dizi olabilir.
Reto Koradi

Bu tek cevapta olduğu için, n=1bu tür özel bir kasayı ilginç görmediğim için soruyu ele almayı gerektirmeyecek şekilde ayarladım .
durron597

3

Haskell, 151 bayt

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

Kullanım örneği:

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

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

Haskell'in tembellikleri sayesinde bu, sürekli bir bellek içinde çalışır. Belli bir yaklaşım kullanır, yani üzerinde döngü yve xarasında seçme *ve bağlı

  • mevcut konum bir diyagonalin üstünde veya altındaysa
  • xResp. yçift ​​ya da garip
  • n/2 çift ​​ya da garip

2

Yaygın Lisp - 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 @))))

Sabit bellek kullanımı ile yinelemeli çözüm. Yukarıda anlatılanlar #n=ve #n#okuyucu değişkenleri yoğun olarak kullanılmaktadır . Daha doğrudan yaklaşımlar olmasına rağmen, burada özyinelemeli bir işlevle başladım ve gotoifadelerle özyinelemeyi simüle etmek için değiştirdim : bu muhtemelen okunamaz.

0 ile 59 arasındaki tüm giriş değerleri için çıkış .

Hata ayıklama bilgileriyle orijinal özyinelemeli sürüm

(not: terprianlamına gelir 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"))
    ))

Örneğin:

(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

Ayrıca , 0'dan 59'a kadar tüm sonuçları olan bu macunlara bakın (yukarıdakiyle aynı değil, bu daha ayrıntılıdır).

Hata ayıklama bilgileri içeren yinelemeli sürüm

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

Bunun bellek kısıtlamasını nasıl karşıladığını açıklayabilir misiniz? Sadece bir özyineleme noktası görüyorum, bu iyi, ama biraz daha ayrıntıya girebilir misin?
durron597

@ durron597 Evet, bunun üzerinde çalışıyorum. Bu şu anda O (n), çünkü işlevi yinelemeli olarak orantılı olarak birkaç zaman nolarak adlandırıyoruz ve çağrı yığını buna göre büyüyor, ancak bu durumda, iki döngü ile özyineleme benzetebiliriz: biri nazalan ve dartan (n <= 3'e kadar ) ve dsıfıra azalan bir tane daha . Şu anda bunun üzerinde çalışacak çok vaktim yok, ancak cevabı buna göre güncellemeye çalışacağım. Btw, spirali yazdırmanın daha doğrudan yolları var, ancak tekrar tekrar tanımlamaya çalışmak eğlenceliydi.
coredump

2

CJam, 72 bayt

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

Bu C çözümümün CJam'e oldukça doğrudan dönüştürülmesidir. Normalde bir CJam çözümünden beklediğiniz kadar kısa değil, ama bu gerçekten bellek kısıtlamasından muzdarip. Sonunda otomatik olarak dökülen yığın üzerinde sonuçlar oluşturmanın ve süslü liste / dize işlemlerini kullanmanın ortak faydaları pencereden dışarı çıkar. Bu, çözümü her seferinde bir karakter üretir ve çıkarır. Yığın çalışma zamanında yalnızca birkaç tamsayı içerir ve sonunda boştur.

Bir golf dili kullanmanın harika bir görüntüsü olmasa da, gösterim daha kompakt olduğu için C kodundan hala oldukça kısadır.

Açıklama:

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.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.