O (log n) 메모리에서 ASCII 나선 인쇄


13

당신은받는 프로그램이나 기능을 쓸 수 있습니다 홀수, 양의 정수를 n , 어디서 n >= 3, STDOUT (또는 시스템에 해당)을 ASCII 나선형으로 두 함수 인수, 명령 줄 인수 또는 STDIN에 (또는 시스템에 해당), 및 인쇄 등 상단 가장자리가 정확히 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이 정수에 맞으면 상수 메모리 사용으로 코딩 될 수 있습니다.
André

1
입력을 저장하는 nlog n약간의 시간이 걸립니다 . 으로 n커질수록, 그래서를 저장하는 데 필요한 공간을 수행합니다. 제한된 수의 변수 로이 작업을 수행하고 있습니까?
xnor

또는 대안으로 제한이 n있습니까?
Sp3000

그는 전체 출력을 한 번에 저장할 수 없다고 말한 다음 한 번에 모두 인쇄하면 더 커질 것이라고 생각합니다. 재귀 적으로 인쇄해야합니다.
Jacob

답변:


9

C, 125 121 바이트

골프 버전 변수가 없습니다 k. 변수 k는 가독성을 높이기 위해 ungolfed 버전에서 사용됩니다. 또한 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(내림)을 두 개의 루프를 실행 외부 일 i에서 -n/2-1n/2+1행을 인쇄 할 ( i=0우리가 얻을 수 있도록 억제 n+1행) 및 내부 하나 j에서 ( -n/2하는 n/2우리가 사용하는 문자를 인쇄 할 수 있습니다.) expression & 1줄무늬를 인쇄 , j*j<i*i세로 또는 가로 줄무늬를 인쇄할지 여부를 결정 하는 조건 (절대 크기 i가 큰 쪽의 세로 , 위쪽 및 아래쪽의 가로). 홀수 +n인지 아닌지에 따라 올바른 종료를 돕기 위해 조정 이 필요합니다. n/2조차.

k는 일반적으로 1이고, 1의 i범위 n/2+1의 절대 값과 j0 의 범위 의 절대 값에 대한 조정을 제공합니다 n/2. k항상 1 이면 동심원 사각형을 얻을 수 있지만 i==j&i<=0대각선 행의 셀이 반전되어 나선을 만들 때 0으로 반전됩니다 .

테스트 프로그램에서 ungolfed

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 크기보다 하나 더 큽니다. 아래쪽 절반에 대해 y에서 1을 빼서 수정하면 본질적으로 가운데 행이 반복됩니다.
  • 사각형을 나선형으로 바꾸려면 y = x + 1대각선을 따라있는 픽셀을 모양의 중간까지 뒤집어 야합니다.

나머지 코드는 코드가 모든 위치에서 단순히 반복되어 각 위치의 중심에서 체비 쇼프 거리를 계산하고 짝수 또는 홀수 거리에 따라 두 문자 중 하나를 방출합니다. 그리고 각 줄의 마지막 위치에 대해 줄 바꿈을 방출합니다.

스칼라 변수가 거의없고 문자가 하나씩 방출되므로 메모리 사용량은 분명히 일정합니다.


훌륭한 답변이지만 초기화하지 않으면 meta.codegolf.stackexchange.com/q/4939/15599의p 파울을 생각합니다 . 함수를 제출할 때 전역 변수를 선언하는 것에 대해서도 확실하지 않습니다. 분명히 내 대답은 내가 이것을하면 4 바이트 더 짧을 것입니다. 메타 게시물 meta.codegolf.stackexchange.com/q/5532/15599를 시작했습니다
Level River St

예, 아마도 초기화해야한다고 생각했습니다 p.
Reto Koradi

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 개의 캐릭터가 있지만, 더 이상 볼 수는 없습니다.

간단한 설명 :

이것은 나선의 선을 중간에 ******가있는 것과 중간에 \ s \ s \ s \ s \ s가있는 두 가지 유형으로 나눕니다. 그런 다음 각 줄이 여러 개의 "*", 중간 및 "*"로 구성되어 있음이 분명합니다. 패턴을 충분히 오랫동안 본다면 각 항목의 수를 정확히 파악할 수 있습니다. 까다로운 것은 기본적으로 조건부를 사용하여 하드 코딩 된 나선형의 중심을 인쇄하는 것이 었습니다. 이것은 *** 및 \ s \ s \ s 라인 스위치가 홀수 / 짝수이기 때문에 유용합니다.

테스트 :

입력 : 55 (큰 것들이 가장 멋지다고 생각합니다)

산출:

***************************************************** *****
                                                      *
***************************************************** *** *
* * *
* ******************************************************* * *
* * * * *
* * *************************************************** * * *
* * * * * * *
* * * ********************************************* * * * *
* * * * * * * * *
* * * * **************************************** * * * * *
* * * * * * * * * * *
* * * * * ************************************ * * * * * *
* * * * * * * * * * * * * *
* * * * * * ***************************** * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * ************************* * * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * * ********************* * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ***************** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * ************* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ********* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ***** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * {-내 프로그램은 여기에 공간을 추가
* * * * * * * * * * * * * * *** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ******* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * *********** * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * *************** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ******************* * * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * * *********************** * * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * * *************************** * * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * ******************************* * * * * * * *
* * * * * * * * * * * * *
* * * * * ************************************** * * * * *
* * * * * * * * * *
* * * * ****************************************** * * * *
* * * * * * * *
* * * ************************************************* * * *
* * * * * *
* * ****************************************************** * *
* * * *
* ******************************************************* ** *
* *
***************************************************** *****

입력: 3

산출:

***
  *
* * 
***

참고 : 저는 컴퓨터 과학자 / CS 학생이 아니며 O (log n) 메모리를 사용한다는 것을 증명하는 방법을 모르겠습니다. 질문의 링크를 기반으로 할 일만 해결할 수 있습니다. 이 답변이 유효한지 누군가가 확인 / 거부 할 수 있다면 감사하겠습니다. 이 답변의 유효성에 대한 나의 논리는 입력 자체를 제외하고 n을 기반으로 한 크기의 변수를 저장하지 않는다는 것입니다. 대신, n 번 실행되는 for 루프는 n을 기준으로 정수 값을 계산합니다. 입력에 관계없이 같은 수의 값이 있습니다.

참고 2 : 중간을 처리하는 방법으로 인해 n = 1에서는 작동하지 않습니다. 이것은 조건부로 쉽게 고칠 수 있으므로 누군가 내 대답의 몇 문자 안에 있으면 고칠 것입니다.)

아이디어를 가지고 놀아보세요.


한 줄에 많은 C ++ 코드를 읽어야하더라도 유효하다고 생각합니다. ;) 당신의 이해가 정확합니다. 에 따라 크기가 다른 메모리는 사용할 수 없습니다 n. 요구 사항을 충족하지 않는 일반적인 예는 완전한 출력 라인을 보유하는 일종의 문자열 / 버퍼 / 배열입니다.
Reto Koradi

이것이 유일한 대답이기 때문에, 나는 n=1특별한 케이스를 흥미롭게 생각하지 않기 때문에 처리를 필요로하지 않도록 질문을 조정했습니다 .
durron597

3

하스켈, 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의 게으름 덕분에 이것은 일정한 메모리 내에서 실행됩니다. 그것은 명백한 접근 방식을 사용, 즉 이상 반복 yx사이에 선택 *에 따라,

  • 현재 위치가 대각선 위 또는 아래 인 경우
  • x각하 y짝수 또는 홀수
  • n/2 짝수 또는 홀수

2

커먼 리스프-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)))))

이것이 메모리 제한을 어떻게 충족시키는 지 설명 할 수 있습니까? 나는 하나의 재귀 지점 만 보지만 좋은 점을 좀 더 자세히 살펴볼 수 있습니까?
durron597

@ durron597 그렇습니다. 우리가 재귀 함수의 시간에 비례 한 수의 호출 때문에 O (n)이 현재 n하나와 : 호출 스택이 따라서 증가하고, 그러나,이 경우에, 우리는 두 개의 루프로 재귀를 시뮬레이션 할 수 n감소 및 dN <= 3까지 (증가 ) 및 d0 으로 감소하는 다른 항목 입니다. 나는 지금이 일을 할 시간이별로 없지만 그에 따라 답변을 업데이트하려고 노력할 것입니다. Btw에는 나선형을 인쇄하는 더 직접적인 방법이 있지만 재귀 적으로 정의하는 것은 재미있었습니다.
코어 덤프

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 솔루션에서 일반적으로 기대하는 것만 큼 짧지는 않지만 실제로 메모리 제한이 있습니다. 마지막에 자동으로 덤프되는 스택에 결과를 작성하고 멋진 목록 / 문자열 작업을 사용하면 얻을 수있는 일반적인 이점은 모두 창 밖으로 나옵니다. 한 번에 한 문자 씩 솔루션을 생성하고 출력합니다. 스택은 런타임에 정수를 몇 개만 포함하며 끝에 비어 있습니다.

골프 언어를 사용하는 것은 좋지 않지만 표기법이 더 작기 때문에 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.