Роздрукуйте спіраль ascii в пам'яті O (log n)


13

Ви можете написати програму або функцію, яка отримує непарне, додатне ціле число n , де n >= 3як аргумент функції, аргументи командного рядка, або на STDIN (або еквівалент для вашої системи) і друкує на STDOUT (або системний еквівалент) спіраль ASCII що обертається всередину за годинниковою стрілкою, де верхній край рівно nдовгих символів. Перший правий край повинен бути n+1символами довгим, очевидно. Наприклад,

Вхід:

11

Вихід:

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

Улов:

  • Ваша програма повинна використовувати не більше O(log n)пам’яті .
  • Ваша програма може друкувати лише символи *(ASCII 42), (ASCII 32), <CR>(ASCII 13) та <LF>(ASCII 10).
  • Ваша програма повинна надрукувати рядок, а не повертати її з функції.
  • Обмеження Великого-висновок тільки по пам'яті , немає ні обмежень на час виконання .
  • Новий рядок не є обов'язковим.
  • Якщо ваша мова не підтримує великі цілі типи, вам не потрібно підтримувати вище, ніж це підтримує, але ви, можливо, не використовуєте це як хитрість, щоб сказати "о, ну, я не повинен підтримувати вище X, так що я може просто зробити величезний масив максимального розміру кожен раз "

Стандартні лазівки заборонені, як зазвичай.


2
Я не вірю, що це можливо. Не можна зберігати вхід nу пам'яті O (1).
xnor

@xnor "O (1) являє собою постійне використання пам'яті. Отже, обсяг вводу є несуттєвим" - Якщо введення n відповідає цілому числу, то я впевнений, що його можна закодувати в постійне використання пам'яті.
Андре

1
Зберігання вводу nзаймає log nбіти. Зі 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(округлення вниз) і запустити дві петлі: зовнішній один iз -n/2-1до n/2+1для друку рядків ( i=0придушене таким чином ми отримуємо n+1рядок) і внутрішні один jз ( -n/2в n/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так, щоб діагональний ряд комірок перевернувся, виробляючи спіраль.

unolfolf у тестовій програмі

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 slash


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

Ключове спостереження полягає в тому, що візерунок - це майже серія концентричних квадратів. З парою невеликих зморшок:

  • Розмір у на один більший, ніж розмір x. Це виправляється відніманням 1 від y для нижньої половини, що по суті повторює середній ряд.
  • Щоб перетворити прямокутники на спіраль, пікселі по y = x + 1діагоналі потрібно перевернути до середини форми.

Для решти код просто перекидається на всі позиції, обчислюючи відстань Чебишева від центру для кожної позиції та випромінюючи один із двох символів залежно від того, чи відстань є парною чи непарною. І випускає новий рядок для останньої позиції кожного рядка.

Оскільки є лише кілька скалярних змінних, і символи випромінюються одна за одною, використання пам'яті, очевидно, постійне.


Відмінна відповідь, але оскільки ви не ініціалізуєте, pя думаю, що ви збиваєтесь з мета.codegolf.stackexchange.com/q/4939/15599 . Я також не впевнений у оголошенні глобальних змінних під час подання функції. Очевидно, моя відповідь була б на 4 байти коротшою, якби я це зробив. Я розпочав мета-повідомлення meta.codegolf.stackexchange.com/q/5532/15599
Level River St

Так, мені зрозуміло, що я, мабуть, повинен ініціалізуватися 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;}

Це не елегантно, але воно не займає багато пам’яті для великих росіян. Крім того, є (майже напевно) близько 20 персонажів, які можна продовжити гольфу, але я не можу більше дивитися на це.

Коротке пояснення:

Це розбиває лінії спіралей на два типи: ті, у яких ****** посередині, і лінії з \ s \ s \ s \ s \ s посередині. Тоді зрозуміло, що кожен рядок складається з декількох "*" s, середини та деяких "*". Зрозуміти, скільки саме кожної речі є простим, якщо дивитися на шаблон досить довго. Хитра річ була надрукувати центр спіралі, яку я в основному важко кодував, використовуючи умовний. Це виявилося корисним, оскільки перемикання ліній *** і \ s \ s \ s є непарними / парними.

Тести:

Вхід: 55 (Я думаю, що великі виглядають круто)

Вихід:

**************************************************** *****
                                                      *
**************************************************** *** *
* * *
* *************************************************** * *
* * * * *
* * *********************************************** * * *
* * * * * * *
* * * ******************************************* * * * *
* * * * * * * * *
* * * * ************************************* * * * * *
* * * * * * * * * * * *
* * * * * ********************************* * * * * * *
* * * * * * * * * * * * *
* * * * * * ***************************** * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * ************************* * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * * ********************* * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * ***************** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * ************* * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * ********* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ***** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * {- моя програма додає пробіл тут btw
* * * * * * * * * * * * * *** * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * ******* * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * *********** * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * *************** * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * ******************* * * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * * *********************** * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * * *************************** * * * * * * *
* * * * * * * * * * * * * *
* * * * * * ******************************* * * * * * *
* * * * * * * * * * * *
* * * * * ************************************* * * * * *
* * * * * * * * * *
* * * * ***************************************** * * * *
* * * * * * * *
* * * ********************************************* * * *
* * * * * *
* * ************************************************* * *
* * * *
* *************************************************** ** *
* *
**************************************************** *****

Вхід: 3

Вихід:

***
  *
* * 
***

Примітка: Я не є вченим-інформатиком / студентом CS, і не знаю, як довести, що для цього використовується O (log n) пам'ять. Я можу лише розробити, що робити на основі посилань у питанні. Буду вдячний, якщо хтось міг би підтвердити / заперечити, якщо ця відповідь справедлива. Моя логіка дійсності цієї відповіді полягає в тому, що вона ніколи не зберігає змінну розміру на основі n, крім самого введення. Натомість цикл for, який працює n разів, обчислює цілі значення на основі n. Існує однакова кількість цих значень незалежно від введених даних.

Примітка2: Це не працює при n = 1 через мій метод роботи з серединою. Це було б легко виправити за допомогою умовних умов, тому якщо хтось знаходиться в межах декількох символів моєї відповіді, я це виправлю;)

Пограйте з ним на ideone.


Я вважаю, що це дійсно, навіть незважаючи на те, що цей 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
***********
          *
********* *
*       * *
* ***** * *
* *   * * *
* * * * * *
* * *** * *
* *     * *
* ******* *
*         *
***********

Завдяки ліні Хаскелл це працює в постійній пам'яті. Він використовує очевидний підхід, тобто цикл над yі , xі вибір між *і , в залежності від

  • якщо поточне положення вище або нижче діагоналі
  • 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зменшенням і 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.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.