在O(log n)内存中打印ascii螺旋


13

您可以编写一个程序来接收一个奇数,正整数 n,其中n >= 3,作为函数参数,命令行参数或在STDIN(或您系统的等效参数)上,并打印到STDOUT(或系统等效参数)ASCII螺旋顺时针向内旋转,顶部边缘恰好是n字符长。n+1显然,第一个右边缘应该是字符长。例如,

输入:

11

输出:

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

收获:

  • 您的程序只能使用O(log n)内存
  • 您的程序只能打印字符*(ASCII 42),(ASCII 32),<CR>(ASCII 13)和<LF>(ASCII 10)。
  • 您的程序必须打印字符串,而不是从函数返回它。
  • 大-O的限制是只能在记忆中,有没有关于限制运行
  • 尾随换行符是可选的。
  • 如果您的语言不支持大整数类型,则您不必支持高于其整数的语言,但是您可以不使用此技巧来表示“哦,好吧,我不必支持X以上,所以我每次都可以将最大的阵列变成巨大的阵列”

像往常一样,禁止出现标准漏洞。


2
我不认为这是可能的。无法将输入存储n在O(1)存储器中。
xnor 2015年

@xnor“ O(1)构成恒定的内存使用量。因此输入量无关紧要”-如果输入n适合整数,那么我敢肯定它可以被编码为恒定的内存使用量。
安德烈·

1
存储输入n需要一些log n位。随着n变大,存储它所需的空间也变大。您也许是说要使用有限数量的变量来执行此操作?
xnor 2015年

或者,是否有限制n
Sp3000

我认为他是说您不能一次存储整个输出,而只能一次打印所有输出,因为那样会变得更大。您可能必须递归打印它。
雅各布

Answers:


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

像示例一样nn+1高螺旋打印宽度。

说明

基本上我一半的值n(四舍五入),并运行两个循环:一个外i-n/2-1n/2+1打印的行(i=0被抑制,所以我们得到n+1行)和内部一个j从(-n/2n/2我们用来打印的字符。)expression & 1打印条纹,以及j*j<i*i决定是否打印垂直或水平条纹的条件(垂直条纹在绝对值i较大的侧面,水平和垂直条纹在顶部和底部。)+n需要进行调整以根据n/2奇数或奇数终止正确的终止甚至。

k通常为1,并且对以下事实进行了调整:绝对值i范围是1至,n/2+1而绝对值j范围是0至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需要将对角线上的像素反转到形状的中间。

对于其余代码,代码只是简单地遍历所有位置,计算每个位置距中心的切比雪夫距离,并根据距离是偶数还是奇数发出两个字符之一。并为每行的最后位置发出换行符。

由于只有几个标量变量,并且字符是一个接一个地发出的,因此内存使用情况显然是恒定的。


很好的答案,但是因为您没有初始化,p所以我认为您对meta.codegolf.stackexchange.com/q/4939/15599感到犯规。我也不确定在提交函数时声明全局变量。显然,如果我这样做,我的答案将短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

输出:

***
  *
* * 
***

注意:我不是计算机科学家/计算机科学专业的学生,​​并且我不知道如何证明它使用了O(log n)内存。我只能根据问题中的链接来确定要做什么。如果有人可以确认/否认此答案是否有效,我将不胜感激。我对这个答案的有效性的逻辑是,除了输入本身之外,它从不存储任何基于n的大小变量。相反,运行n次的for循环会根据n计算整数值。无论输入内容如何,​​这些值的数量均相同。

注意2:由于我处理中间值的方法,因此不适用于n = 1。这很容易用条件语句解决,因此,如果有人在我的答案的几个字符范围内,我会解决它;)

在ideone上玩。


我相信它是有效的,即使必须在一行上读取这么多的C ++代码。;)您的理解是正确的。您不能使用任何大小取决于的内存n。一个不满足要求的典型示例将是某种字符串/缓冲区/数组,它们保留完整的输出行。
Reto Koradi

由于这是唯一的答案,因此我已调整了问题,无需进行处理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的懒惰,它可以在恒定的内存中运行。它使用明显的方法,即遍历yand x并在*和之间进行选择,取决于

  • 如果当前位置在对角线之上或之下
  • x分别 y是偶数还是奇数
  • n/2 是偶数还是奇数

2

普通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)))))

您能解释一下如何满足内存限制吗?我只看到一个递归点,这很好,但是您可以再详细一点吗?
durron597

@ durron597是的,我正在为此工作。当前为O(n),因为我们以n与时间成比例的时间递归调用函数,并且调用堆栈相应地增长,但是在这种情况下,我们可以通过两个循环来模拟递归:一个n递减并d递增(直到n <= 3 ),另一个d递减为零。我现在没有太多时间来处理此问题,但是我将尝试相应地更新答案。顺便说一句,有更多直接的方法可以打印螺旋线,但是尝试递归定义它很有趣。
coredump

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.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.