O(log n)メモリにASCIIスパイラルを出力します


13

奇数の正の整数 を受け取るプログラムまたは関数を作成できますn。ここでn >= 3、関数引数、コマンドライン引数として、またはSTDIN(またはシステムに相当)として、ASCIIスパイラルをSTDOUT(またはシステムに相当)に出力します。どこそれは内側に時計回りに回転するトップエッジが正確であるn文字。最初の右端n+1は明らかに文字数でなければなりません。例えば、

入力:

11

出力:

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

キャッチ:

  • あなたのプログラムはO(log n)メモリ以上を使用してはいけません。
  • プログラムは、文字*(ASCII 42)、(ASCII 32)、<CR>(ASCII 13)、および<LF>(ASCII 10)のみを印刷できます。
  • プログラムは、関数から文字列を返すのではなく、文字列を印刷する必要があります。
  • Big-Oの制限はメモリのみに適用され、実行時制限はありません
  • 末尾の改行はオプションです。
  • あなたの言語が大きな整数型をサポートしていない場合、サポートしているものよりも高いものをサポートする必要はありませんが、これを「ああ、まあ、私は上記のXをサポートする必要はありません」毎回巨大な配列を最大サイズにすることができます」

通常の標準的な抜け穴は禁止されています。


2
これが可能だとは思わない。入力nをO(1)メモリに保存することはできません。
-xnor

@xnor "O(1)は一定のメモリ使用量を構成します。したがって、入力量は重要ではありません"-入力nが整数に収まる場合、一定のメモリ使用量にコーディングできると確信しています。
アンドレ

1
入力の保存にnlog n少し時間がかかります。ASがn大きくなるので、それを格納するために必要なスペースがありません。おそらく、限られた数の変数でこれを行うと言っていますか?
xnor

または、代わりに、制限がありnますか?
Sp3000

彼はあなたが出力全体を一度に保存することはできないと言っていると思います。おそらく再帰的に印刷する必要があります。
ジェイコブ

回答:


9

C、125の 121バイト

ゴルフバージョンこれには変数がありませんk。この変数kは、読みやすくするために、非ゴルフバージョンで使用されています。また、forループ条件が再配置され、不要なセットが{}削除されます。別のセットは、初期化位置にあるループの括弧内を{}移行することで削除できますが、これは出力の先頭に改行があることを意味するため、まだ実行していません。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("");}}}

例のようにn幅の広いn+1高スパイラルを印刷します。

説明

基本的に私は、の値が半分n(切り捨て)をし、二つのループを実行します。外側の1をiから-n/2-1n/2+1行を印刷するには(i=0我々が得るよう抑制されているn+1行)と内側の1 jから(-n/2までn/2私たちが使用する文字を印刷する。)expression & 1ストライプを印刷します、およびj*j<i*i縦縞または横縞を印刷するかどうかを決定する条件(絶対絶対値iが大きい側は垂直、上下は水平)。奇数か奇数+nかに応じて、正しい終了を支援する調整が必要です。n/2でも。

kは通常1であり、i範囲1からn/2+1の絶対値がj0からの範囲の絶対値であるという事実に対する調整を提供しn/2ます。k常に1だった場合、同心の長方形が得られますが、0に反転するとi==j&i<=0セルの対角列が反転してスパイラルが生成さよう。

テストプログラムに参加していない

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

出力

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

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

3
***
  *
* *
***

1
*
*

少しだけ私を倒してください... +1これは非常に短いです!
sudo rm -rfスラッシュ


7

C、118バイト

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

最終ゴルフ前のコード:

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

重要な観察は、パターンがほぼ同心の正方形のシリーズであることです。わずかなしわがいくつかあります:

  • yサイズはxサイズよりも1つ大きくなります。これは、下半分のyから1を引くことで修正され、基本的に中央の行が繰り返されます。
  • 長方形をスパイラルにするには、y = x + 1対角線に沿ったピクセルを形状の中央まで反転させる必要があります。

残りについては、コードはすべての位置を単純にループし、各位置の中心からのチェビシェフ距離を計算し、距離が偶数か奇数かに応じて2つの文字のいずれかを放出します。そして、各行の最後の位置に改行を発行します。

スカラー変数は少数であり、文字が1つずつ出力されるため、メモリ使用量は明らかに一定です。


優れた答えですが、初期化しないpと、meta.codegolf.stackexchange.com / q / 4939 / 15599に違反すると思います。また、関数を送信するときにグローバル変数を宣言するかどうかもわかりません。これを行うと、明らかに私の答えは4バイト短くなります。私は、メタポスト始めましたmeta.codegolf.stackexchange.com/q/5532/15599
レベル川セント

はい、おそらく初期化する必要があると思いましたp
レトコラディ

3

C ++、926バイト

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

これはエレガントではありませんが、大きなnに対して多くのメモリを消費しません。さらに、(ほぼ確実に)さらにゴルフをすることができる約20のキャラクターがありますが、私はもうそれを見ることができません。

簡単な説明:

これにより、スパイラル内の行が2つのタイプに分割されます。中央に******が付いた行と、中央に\ s \ s \ s \ s \ sが付いた行 次に、各行が複数の「*」、中央、および「*」で構成されていることは明らかです。パターンを十分に長く見れば、それぞれのことの正確な数を把握するのは簡単です。トリッキーなことは、スパイラルの中心を印刷することで、これを基本的に条件付きでハードコーディングしました。***と\ s \ s \ sの行が奇数/偶数に切り替わるので、これは結局有用でした。

テスト:

入力:( 55大きなものが一番かっこいいと思う)

出力:

************************************************** *****
                                                      *
************************************************** *** *
* * *
* ************************************************* * *
* * * * *
* * ********************************************* * * *
* * * * * * *
* * * ***************************************** * * * *
* * * * * * * * *
* * * * ************************************* * * * * *
* * * * * * * * * * *
* * * * * ********************************* * * * * * *
* * * * * * * * * * * * *
* * * * * * ***************************** * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * ************************* * * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * * * ********************* * * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * * * ***************** * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ************* * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * ********* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ***** * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *私のプログラムはここにスペースを追加します
* * * * * * * * * * * * * *** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ******* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * *********** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * *************** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * ******************* * * * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * * *********************** * * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * *************************** * * * * * * *
* * * * * * * * * * * * * *
* * * * * * ******************************* * * * * * *
* * * * * * * * * * * *
* * * * * *********************************** * * * * *
* * * * * * * * * *
* * * * *************************************** * * * *
* * * * * * * *
* * * ******************************************* * * *
* * * * * *
* * *********************************************** * *
* * * *
* ************************************************* ** *
* *
***************************************************** *****

入力: 3

出力:

***
  *
* * 
***

注:私はコンピューター科学者/ CSの学生ではなく、これがO(log n)メモリーを使用していることを証明する方法がわかりません。私は質問のリンクに基づいて何をすべきかを解決することができます。この回答が有効である場合、誰かが確認/拒否できれば感謝します。この答えの妥当性の私の論理は、入力自体を除き、nに基づくサイズの変数を格納しないことです。代わりに、n回実行されるforループは、nに基づいて整数値を計算します。入力に関係なく、これらの値の数は同じです。

注2:これは、n = 1では機能しません。これは、中間を処理する私の方法のためです。これは条件付きで簡単に修正できるので、誰かが私の答えの数文字以内にいる場合は修正します;)

イデオンで遊んでください。


1行でこれだけのC ++コードを読む必要があったとしても、それは有効だと思います。;)あなたの理解は正しいです。に依存するサイズのメモリは使用できませんn。要件を満たさない典型的な例は、出力の完全な行を保持する何らかの種類の文字列/バッファ/配列です。
レトコラディ

これが唯一の答えであるため、このn=1ような特別なケーシングを興味深いとは思わないため、処理を必要としないように質問を調整しました。
durron597

3

Haskell、151バイト

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

使用例:

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

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

Haskellの怠lazのおかげで、これは一定のメモリ内で実行されます。それは明白なアプローチを使用し、すなわちオーバーループyxの間で選択します*に応じて、

  • 現在の位置が対角線の上または下にある場合
  • x それぞれ y偶数または奇数
  • n/2 偶数または奇数

2

Common 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 @))))

メモリ使用量が一定の反復ソリューション。上記では、#n=#n#変数とリーダー変数ます。より直接的なアプローチはありますが、ここでは再帰関数から始め、再帰をシミュレートするように修正しましたgotoステートメント。これはおそらく読みにくいです。

0〜59のすべての入力値の出力

デバッグ情報を含む元の再帰バージョン

(注:terpriはを意味します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"))
    ))

例えば:

(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

0〜59のすべての結果を含むこのペーストも参照してください(上記と同じではなく、これはより冗長です)。

デバッグ情報付きの反復バージョン

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

これがメモリ制限をどのように満たしているか説明できますか?再帰ポイントは1つしかありませんが、これは良いことですが、もう少し詳しく説明していただけますか?
durron597

@ durron597はい、私はこれに取り組んでいます。我々は時間に比例多数の再帰関数を呼び出すので、これは、現在、O(N)であるnといずれかとコールスタックがそれに応じて成長するが、この場合において、我々は、2つのループを再帰をシミュレートすることができn減少とd<= 3 Nまで(増加します)、およびdゼロに減少する別の1つ。今はこれに取り組む時間はあまりありませんが、それに応じて答えを更新してみます。ところで、スパイラルを印刷するより直接的な方法がありますが、それを再帰的に定義するのは楽しかったです。
コアダンプ

2

CJam、72バイト

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

これは、CソリューションからCJamへのかなり直接的な変換です。CJamソリューションから通常期待するほど短くはありませんが、これは実際にメモリ制限の影響を受けます。最後に自動的にダンプされるスタックに結果を構築することの一般的な利点と、派手なリスト/文字列操作を使用すると、すべてウィンドウの外に出ます。これにより、一度に1文字ずつソリューションが生成および出力されます。スタックには実行時に少数の整数のみが含まれ、最後には空になります。

ゴルフ言語を使用することの優れた表示ではありませんが、表記がよりコンパクトであるという理由だけで、Cコードよりもかなり短くなっています。

説明:

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.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.