Narysuj Diabelskie Schody


46

Przez diabła Klatka jest wstęga podobny funkcji związanej zbioru Cantora.

wprowadź opis zdjęcia tutaj

Twoim zadaniem jest odtworzenie tej funky - w sztuce ASCII!

Wejście

Pojedyncza liczba całkowita n >= 0wskazująca rozmiar wyjścia. Dane wejściowe można podać za pomocą STDIN, argumentu funkcji lub argumentu wiersza poleceń.

Wynik

Odbitka ASCII-art schodów diabła w rozmiarze n, zwrócona w postaci łańcucha lub wydrukowana do STDOUT. Końcowe spacje na końcu każdego rzędu są w porządku, ale spacje wiodące nie. Możesz opcjonalnie wydrukować jedną końcową linię nowego wiersza.

W przypadku rozmiaru 0wynik jest po prostu:

x

(Jeśli chcesz, możesz użyć dowolnego innego znaku ASCII do wydruku innego niż spacja, zamiast x.)

Dla rozmiaru n > 0:

  • Weź wynik wielkości n-1i rozciągnij każdy rząd trzy razy
  • Riffle między rzędami pojedynczych xs
  • Przesuń rzędy w prawo, aby xw każdej kolumnie znajdował się dokładnie jeden , a pozycja pierwszego xjest minimalna, a jednocześnie maleje wraz z rzędami

Na przykład dane wyjściowe dla n = 1:

    x
 xxx
x

Aby uzyskać wynik n = 2, rozciągamy każdy wiersz trzy razy:

            xxx
   xxxxxxxxx
xxx

Riffle między rzędami singli x:

x
            xxx
x
   xxxxxxxxx
x
xxx
x

Przesuń w prawo:

                  x
               xxx
              x
     xxxxxxxxx
    x
 xxx
x

Jako kolejny przykład tutaj jest n = 3.

Punktacja

To jest golf golfowy, więc wygrywa rozwiązanie w najmniejszej liczbie bajtów.

Odpowiedzi:


7

Pyth, 30

jb_u+G+*leGd*HNu+N+^3hTNUQ]1]k

Jest to program, który pobiera dane wejściowe ze STDIN i wykorzystuje metodę znalezienia zestawu Cantor przez grc. Używa znaku „do wyświetlenia krzywej.

Wypróbuj online tutaj.

Wyjaśnienie:

Wyjaśnię kod w dwóch częściach, po pierwsze, generacji zestawu kantorów:

u+N+^3hTNUQ]1
u        UQ]1         : reduce( ... , over range(input), starting with [1])
 +N                   : lambda N,T: N + ...
   +^3hTN             : 3 ** (T+1) + N   (int + list in pyth is interpreted as [int] + list)

I formatowanie wyjściowe:

jb_u+G+*leGd*HN    ]k
jb_                    : "\n".join(reversed(...)
   u               ]k  : reduce(lambda G,H: ... , over cantor set, starting with [""])
    +G+*leGd           : G + len(G[-1]) * " " + ...
            *HN        : H * '"'

Zauważ, że domyślnie w pyth N = '”.


32

J ( 73 68 58 41 39 38 35 34 znaków)

Po pewnym czasie zastanowienia się nad problemem znalazłem zupełnie inny sposób na generowanie wzoru Schodka Diabła. Stara odpowiedź wraz z jej wyjaśnieniem została usunięta. Możesz sprawdzić wersje tej odpowiedzi, aby dowiedzieć się, jak to było.

Ta odpowiedź zwraca tablicę pustych miejsc i ostrych narzędzi reprezentujących schody diabła.

' #'{~1(]|.@=@#~[:,3^q:)2}.@i.@^>:

Oto odpowiedź podzielona na dwie części w wyraźnym zapisie:

f =: 3 : '|. = (, 3 ^ 1 q: y) # y'
g =: 3 : '(f }. i. 2 ^ >: y) { '' #'''

Wyjaśnienie

Podejście jest nieco inne, więc obserwujcie i bądźcie zaskoczeni.

  1. >: 3 - trzy inkrementowane, to znaczy,

    4
    
  2. 2 ^ >: 3 - dwa do potęgi trzech inkrementowanych, to znaczy,

    16
    
  3. i. 2 ^ >: 3- pierwsze 2 ^ >: 3liczby całkowite, to znaczy,

    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    
  4. }. i. 2 ^ 4- pierwsze 2 ^ >: 3liczby całkowite, ścięte, to znaczy,

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    

    Nazwijmy tę sekwencję s; wchodzimy fteraz.

  5. 1 q: s- wykładniki liczby 2 w podstawowym rozkładzie każdej pozycji z s. Zasadniczo x q: yzwraca tabelę wykładników pierwszych xliczb pierwszych w rozkładzie pierwszych liczb y. Daje to:

    0
    1
    0
    2
    0
    1
    0
    3
    0
    1
    0
    2
    0
    1
    0
    
  6. 3 ^ 1 q: s - trzy do potęgi tych wykładników, to znaczy,

     1
     3
     1
     9
     1
     3
     1
    27
     1
     3
     1
     9
     1
     3
     1
    
  7. , 3 ^ 1 q: s- ravel (tzn. Argument, że jego struktura jest zwinięta w wektor) poprzedniego wyniku. Jest to konieczne, ponieważ q:wprowadza niechcianą oś wleczoną. To daje

     1 3 1 9 1 3 1 27 1 3 1 9 1 3 1
    
  8. (, 3 ^ 1 q: s) # s- każdy element sreplikowany tak często, jak odpowiadający mu element w poprzednim wyniku, to znaczy,

    1 2 2 2 3 4 4 4 4 4 4 4 4 4 5 6 6 6 7 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9 10 10 10 11 12 12 12 12 12 12 12 12 12 13 14 14 14 15
    
  9. = (, 3 ^ 1 q: s) # s - samoklasyfikacja poprzedniego wyniku, tj. Macierz, w której każdy wiersz reprezentuje jeden z unikalnych elementów argumentu, każda kolumna reprezentuje odpowiedni element argumentu, a każda komórka reprezentuje, czy elementy wiersza i kolumny są równe, to jest,

    1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
    
  10. |. = (, 3 ^ 1 q: s) # s - poprzedni wynik obrócony wzdłuż osi pionowej.

  11. (|. = (, 3 ^ 1 q: s) # s) { ' #'- elementy poprzedniego wyniku użyte jako wskaźniki w tablicy ' #', więc 0są zastępowane  i 1zastępowane przez #, to znaczy,

                                                                    #
                                                                 ### 
                                                                #    
                                                       #########     
                                                      #              
                                                   ###               
                                                  #                  
                       ###########################                   
                      #                                              
                   ###                                               
                  #                                                  
         #########                                                   
        #                                                            
     ###                                                             
    #      
    

    wynik, który chcemy.


Wewnątrz pętli zasilania (,],~3^#@~.)@]zamiast (1,[:,1,"0~3*]) oszczędzać 1 bajt. A jeśli nie masz nic przeciwko !jako wyjściowy znak char, u:32+zamiast ' #'{~zapisywać inny.
randomra

#\ zamiast i.@#i wyprzedzasz APL! :)
randomra

Twoje drugie rozwiązanie nie działa, ponieważ potrzebny byłby limit, ale znalazłem inny sposób na pokonanie APL.
FUZxxl

Nowa produkcja to schody dla n-1nie dla n.
randomra

@andomra Ah… to gówno. Pokażę, czy można to naprawić.
FUZxxl,

26

Sześciokąty , 217 bajtów

To była niesamowita zabawa. Dziękujemy za opublikowanie tego wyzwania.

Pełne ujawnienie: język (heksagonia) nie istniał w momencie opublikowania tego wyzwania. Jednak nie wymyśliłem go, a język nie został zaprojektowany do tego wyzwania (ani żadnego innego konkretnego wyzwania).

){_2"_{\"{{""}"{'2//_.\><*\"\/_><[\]/3\'\_;|#__/(\2\'3_'}(#:|{$#{>_\//(#={/;01*&"\\_|[##={|}$_#></)]$_##|){*_.>.(/?#//~-="{}<_"=#/\}.>"%<.{#{x\"<#_/=&{./1#_#>__<_'\/"#|@_|/{=/'|\"".{/>}]#]>(_<\'{\&#|>=&{{(\=/\{*'"]<$_

Rozłożony sześciokątnie:

        ) { _ 2 " _ { \ "
       { { " " } " { ' 2 /
      / _ . \ > < * \ " \ /
     _ > < [ \ ] / 3 \ ' \ _
    ; | # _ _ / ( \ 2 \ ' 3 _
   ' } ( # : | { $ # { > _ \ /
  / ( # = { / ; 0 1 * & " \ \ _
 | [ # # = { | } $ _ # > < / ) ]
$ _ # # | ) { * _ . > . ( / ? # /
 / ~ - = " { } < _ " = # / \ } .
  > " % < . { # { x \ " < # _ /
   = & { . / 1 # _ # > _ _ < _
    ' \ / " # | @ _ | / { = /
     ' | \ " " . { / > } ] #
      ] > ( _ < \ ' { \ & #
       | > = & { { ( \ = /
        \ { * ' " ] < $ _

Program tak naprawdę nie korzysta z #instrukcji, więc użyłem tego znaku, aby pokazać, które komórki są rzeczywiście nieużywane.

Jak działa ten program? To zależy. Chcesz krótką wersję, czy długą?

Krótkie wyjaśnienie

Aby zilustrować, co rozumiem przez „linię” i „segment” w poniższym wyjaśnieniu, rozważ ten podział zamierzonego wyniku:

segments →
 │   │ │         │ │   │x   lines
─┼───┼─┼─────────┼─┼───┼─     ↓
 │   │ │         │ │xxx│
─┼───┼─┼─────────┼─┼───┘
 │   │ │         │x│
─┼───┼─┼─────────┼─┘
 │   │ │xxxxxxxxx│
─┼───┼─┼─────────┘
 │   │x│
─┼───┼─┘
 │xxx│
─┼───┘
x│

Po wyjaśnieniu program odpowiada następującemu pseudokodowi:

n = get integer from stdin

# Calculate the number of lines we need to output.
line = pow(2, n+1)

while line > 0:
    line = line - 1

    # For all segments except the last, the character to use is spaces.
    ch = ' ' (space, ASCII 32)

    # The number of segments in each line is
    # equal to the line number, counting down.
    seg = line

    while seg > 0:
        seg = seg - 1

        # For the last segment, use x’s.
        if seg = 0:
            ch = 'x' (ASCII 120)

        # Calculate the actual segment number, where the leftmost is 1
        n = line - seg

        # Output the segment
        i = pow(3, number of times n can be divided by 2)
        i times: output ch

    output '\n' (newline, ASCII 10)

end program

Długie wyjaśnienie

Zapoznaj się z tym schematem ścieżki kodu zakodowanym kolorem

Ścieżka wykonania

Realizacja rozpoczyna się w lewym górnym rogu. Sekwencja instrukcji ){2'"''3''"2}?)jest wykonywana (plus kilka zbędnych anulowań, "{itp.) Poprzez podążanie dość skomplikowaną ścieżką. Zaczynamy od wskaźnika instrukcji nr 0, zaznaczonego szkarłatem. W połowie przełączamy się na numer 1, zaczynając od prawego górnego rogu i pomalowany na zielono. Gdy IP # 2 zaczyna się od błękitu chabrowego (środkowy prawy), układ pamięci jest następujący:

Układ pamięci

W całym programie krawędzie oznaczone 2a i 2b zawsze będą miały wartość 2(używamy ich do obliczania odpowiednio 2ⁿ⁺¹ i dzielenia przez 2), a krawędź oznaczona 3 zawsze będzie 3(używamy tego do obliczania 3ⁱ).

Wchodzimy do biznesu, wchodząc w naszą pierwszą pętlę, wyróżnioną kolorem chabrowym. Ta pętla wykonuje instrukcje, (}*{=&}{=aby obliczyć wartość 2ⁿ⁺¹. Kiedy pętla wychodzi, wybierana jest brązowa ścieżka siodła, która prowadzi nas do wskaźnika instrukcji nr 3. Ten adres IP jedynie przesuwa się wzdłuż dolnej krawędzi na zachód w żółtawo-złotym kolorze i wkrótce przechodzi kontrolę nad IP # 4.

Ścieżka fuksji wskazuje, w jaki sposób IP # 4, zaczynając od lewego dolnego rogu, przechodzi szybko do linii zmniejszania , ustawia ch na 32(znak spacji) i seg na (nowa wartość) linii . Jest to spowodowane wczesnym zmniejszeniem, że faktycznie zaczynamy od 2ⁿ⁺¹ − 1 i ostatecznie doświadczamy ostatniej iteracji z wartością 0. Następnie wchodzimy do pierwszej zagnieżdżonej pętli.

Zwracamy uwagę na rozgałęziony indygo, w którym po krótkim zmniejszeniu seg widzimy, że ch jest aktualizowane xtylko wtedy, gdy seg wynosi teraz zero. Następnie n ustawia się na linii - seg, aby określić faktyczną liczbę segmentów, w których się znajdujemy. Natychmiast wchodzimy w kolejną pętlę, tym razem w jasnym kolorze pomidora.

W tym miejscu ustalamy, ile razy n (bieżący numer segmentu) można podzielić przez 2. Tak długo, jak moduł daje nam zero, zwiększamy i dzielimy n przez 2. Gdy jesteśmy usatysfakcjonowani, n nie jest już podzielny , rozgałęziamy się na szary łupek, który zawiera dwie pętle: najpierw podnosi 3 do potęgi obliczonego przez nas i , a następnie wyprowadza ch tyle razy. Zauważ, że pierwsza z tych pętli zawiera[instrukcja, która przełącza sterowanie na IP # 3 - ta, która wcześniej tylko stawiała małe kroki wzdłuż dolnej krawędzi. Ciało pętli (pomnożenie przez 3 i zmniejszenie) jest wykonywane przez samotny adres IP # 3, uwięziony w nieskończonym cyklu ciemnozielonej zieleni wzdłuż dolnej krawędzi kodu. Podobnie, druga z tych łupkowych szarych pętli zawiera ]instrukcję, która aktywuje IP # 5 do wyjścia ch i dekrementacji, pokazane tutaj w ciemno indyjskiej czerwieni. W obu przypadkach wskaźniki instrukcji uwięzione w służebności posłusznie wykonują jedną iterację naraz i poddają kontrolę z powrotem do IP # 4, tylko po to, aby poczekać chwilę na ponowne wezwanie ich usługi. Tymczasem szary łupek powraca do braci w kolorze fuksji i indygo.

Gdy seg nieuchronnie osiąga zero, pętla indygo wychodzi na zieloną ścieżkę trawnika, która jedynie wyprowadza znak nowej linii i natychmiast łączy się z powrotem w fuksję, aby kontynuować pętlę linii . Poza końcową iteracją pętli liniowej znajduje się krótka, ebonowa ścieżka ostatecznego zakończenia programu.


8
Teraz jest to po prostu staromodny szaleństwo.
FUZxxl

21

Python 2, 78

L=[1]
i=3
exec"L+=[i]+L;i*=3;"*input()
while L:x=L.pop();print' '*sum(L)+'x'*x

Zaczynając od listy L=[1], kopiujemy ją i wstawiamy kolejną potęgę 3 na środku, w wyniku czego [1, 3, 1]. Jest to powtarzane nrazy, aby podać nam długości rzędów schodów diabła. Następnie drukujemy każdy wiersz wypełniony spacjami.


20

APL, 38

⊖↑'x'/⍨¨D,⍨¨0,¯1↓-+\D←{1,⍨∊1,⍪3×⍵}⍣⎕,1

Przykład:

      ⊖↑'x'/⍨¨D,⍨¨0,¯1↓-+\D←{1,⍨∊1,⍪3×⍵}⍣⎕,1
⎕:
      2
                  x
               xxx 
              x    
     xxxxxxxxx     
    x              
 xxx               
x   

Wyjaśnienie:

⊖↑'x'/⍨¨D,⍨¨0,¯1↓-+\D←{1,⍨∊1,⍪3×⍵}⍣⎕,1

                                     ⎕       ⍝ read a number from the keyboard
                       {           }⍣ ,1      ⍝ apply this function N times to [1]
                               3×⍵           ⍝ multiply each value by 3
                           ∊1,⍪               ⍝ add an 1 in front of each value
                        1,⍨                  ⍝ add an 1 to the end
                     D←                      ⍝ store values in D (lengths of rows)
                   +\                        ⍝ get running sum of D
                  -                          ⍝ negate (negative values on / give spaces)
             0,¯1↓                           ⍝ remove last item and add a 0 to the beginning
                                             ⍝ (each row needs offset of total length of preceding rows)   
         D,⍨¨                                ⍝ join each offset with each row length
   'x'/⍨¨                                    ⍝ get the right number of x-es and spaces for each row
 ↑                                           ⍝ make a matrix out of the rows
⊖                                            ⍝ mirror horizontally 

To miłe rozwiązanie.
FUZxxl

20
Uwielbiam to, że wyjaśnienie kodu wygląda jak Diabelskie Schody.
Alex A.

Znalazłem jeszcze krótsze rozwiązanie APL.
FUZxxl,

14

GNU sed, 142

Nie najkrótsza odpowiedź, ale sed !:

s/$/:/
:l
s/x/xxx/g
s/:/:x:/g
tb
:b
s/^1//
tl
s/:x/X/g
s/^/:/
:m
s/.*:([Xx]+)Xx*:$/&\1:/
tm
:n
s/([ :])[Xx](x*Xx*)/\1 \2/g
tn
s/:/\n/g
s/X/x/g

Ponieważ jest to sed (brak natywnej arytmetyki), biorę swobody z regułą „Pojedyncza liczba całkowita n> = 0, wskazująca rozmiar wyniku” . W takim przypadku wejściowa liczba całkowita musi być ciągiem 1s, którego długość wynosi n. Myślę, że to „wskazuje” rozmiar wyjścia, nawet jeśli nie jest to bezpośredni numeryczny odpowiednik n. Zatem dla n = 2 ciągiem wejściowym będzie 11:

$ echo 11 | sed -rf devils-staircase.sed

                  x
               xxx
              x
     xxxxxxxxx
    x
 xxx
x

$ 

Wydaje się, że kończy się to wykładniczą złożonością czasową O ( cn ), gdzie c wynosi około 17. n = 8 zajęło mi około 45 minut.


Alternatywnie, jeśli wymagane jest, aby n było wpisane dokładnie numerycznie, możemy to zrobić:

sed, 274 bajty

s/[0-9]/<&/g
s/9/8Z/g
s/8/7Z/g
s/7/6Z/g
s/6/5Z/g
s/5/4Z/g
s/4/3Z/g
s/3/2Z/g
s/2/1Z/g
s/1/Z/g
s/0//g
:t
s/Z</<ZZZZZZZZZZ/g
tt
s/<//g
s/$/:/
:l
s/x/xxx/g
s/:/:x:/g
tb
:b
s/^Z//
tl
s/:x/X/g
s/^/:/
:m
s/.*:([Xx]+)Xx*:$/&\1:/
tm
:n
s/([ :])[Xx](x*Xx*)/\1 \2/g
tn
s/:/\n/g
s/X/x/g

Wynik:

$ echo 2 | sed -rf devils-staircase.sed

                  x
               xxx
              x
     xxxxxxxxx
    x
 xxx
x

$ 

7
To jest naprawdę fajne.
FUZxxl,

8

Python 2, 81

def f(n,i=1,s=0):
 if i<2<<n:q=3**len(bin(i&-i))/27;f(n,i+1,s+q);print' '*s+'x'*q

Wersja programu (88)

def f(n,s=0):
 if n:q=3**len(bin(n&-n))/27;f(n-1,s+q);print' '*s+'x'*q
f((2<<input())-1)

Liczba x w n1 rzędzie z indeksowaniem wynosi 3 do potęgi (indeks pierwszego ustawionego bitu n, zaczynając od lsb).


8

Python 2, 74

def f(n,s=0):
 if~n:B=3**n;A=s+B-2**n;f(n-1,A+B);print' '*A+'x'*B;f(n-1,s)

Podejście rekurencyjne. Schody diabła o wielkości $ n $ są podzielone na trzy części

  • Lewa rekurencyjna gałąź, schody wielkości n-1, których długość wynosi3**n - 2**n
  • Linia środkowa x„długości3**n
  • Prawa rekurencyjna gałąź, schody wielkości n-1, których długość wynosi3**n - 2**n

Zauważ, że całkowita długość trzech części wynosi 3*(3**n) - 2*(2**n)lub 3**(n+1) - 2**(n+1), co potwierdza indukcję.

Opcjonalna zmienna sprzechowuje przesunięcie bieżących części, które drukujemy. Najpierw cofamy się do lewej gałęzi z większym przesunięciem, następnie drukujemy linię środkową, a następnie wykonujemy prawą gałąź przy bieżącym przesunięciu.


6

CJam, 36 35 33 bajtów

Oto inne podejście do CJam (nie spojrzałem na kod Optimizera, więc nie wiem, czy tak naprawdę jest inaczej):

L0sl~{{3*0s}%0s\+}*{1$,S*\+}%W%N*

Wykorzystuje 0to krzywą. Alternatywnie (przy użyciu sztuczki grc)

LLl~){3\#a1$++}/{1$,S*\'x*+}%W%N*

który wykorzystuje x.

Sprawdź to tutaj.

Wyjaśnienie

Podstawową ideą jest, aby najpierw utworzyć tablicę z wierszami, jak

["0" "000" "0" "000000000" "0" "000" "0"]

Następnie przejrzyj tę listę, przygotowując odpowiednią ilość spacji.

L0sl~{{3*0s}%0s\+}*{1$,S*\+}%W%N*
L                                 "Push an empty string for later.";
 0s                               "Push the array containing '0. This is the base case.";
   l~                             "Read and evaluate input.";
     {           }*               "Repeat the block that many times.";
      {    }%                     "Map this block onto the array.";
       3*                         "Triple the current string.";
         0s                       "Push a new zero string.";
             0s\+                 "Prepend another zero string.";
                   {       }%     "Map this block onto the result.";
                    1$            "Copy the last line.";
                      ,S*         "Get its length and make a string with that many spaces.";
                         \+       "Prepend the spaces to the current row.";
                             W%   "Reverse the rows.";
                               N* "Join them with newlines.";

Druga wersja działa podobnie, ale tworzy tablicę długości

[1 3 1 9 1 3 1]

A potem zamienia to w ciągi xs na ostatecznej mapie.


6

Dyalog APL, 34 znaki

Korzystanie z podejścia grc. Rysuje schody za pomocą znaków (domino) i pobiera dane wejściowe ze standardowego wejścia. Takie rozwiązanie zakłada ⎕IO←0.

' ⌹'[(∪∘.=⊖){⍵/⍳≢⍵}⊃(⊢,,)/3*⌽⍳1+⎕]
  • - pobrać dane wejściowe ze standardowego wejścia.
  • ⌽⍳1+⎕- sekwencja liczb od 0 do 0. (np. 3 2 1 0)
  • 3*⌽⍳1+⎕- trzy do potęgi tego (np. 27 9 3 1)
  • (⊢,,)/3*⌽⍳1+⎕- poprzedni wynik złożony z prawej strony przez funkcję milczącą, ⊢,,która jest równa dfn, {⍵,⍺,⍵}dająca długość kroku schodów diabła według podejścia grc.
  • {⍵/⍳≢⍵}⊃(⊢,,)/3*⌽⍳1+⎕ długości kroków przeliczane na kroki.
  • (∪∘.=⊖){⍵/⍳≢⍵}⊃(⊢,,)/3*⌽⍳1+⎕że samo ogłoszenie, jak w moim roztworu J . Zauważ, że już poprawnie odwraca wynik.
  • ' ⌹'[(∪∘.=⊖){⍵/⍳≢⍵}⊃(⊢,,)/3*⌽⍳1+⎕] liczby zastąpione spacjami i domino.

4

Ruby, 99

Inna odpowiedź na moją drugą, inspirowana odpowiedzią FUZxxl

FUZxxl zauważa, że ​​liczby x odpowiadają liczbie czynników 2 indeksu. na przykład dla n = 2 mamy następującą faktoryzację:

1 =1
2 =1 * 2
3 =3
4 =1 * 2 * 2
5 =5
6 =3 * 2
7 =7

Używam raczej prostszego sposobu wyodrębnienia tych potęg 2: i=m&-mco daje sekwencję 1 2 1 4 1 2 1itp. Działa to w następujący sposób:

m-1jest taki sam, jak mw najbardziej znaczących bitach, ale najmniej znaczący bit 1 staje się zerem, a wszystkie zera po prawej stają się 1.

Aby móc ORAZ to z oryginałem, musimy odwrócić bity. Można to zrobić na różne sposoby. Jednym ze sposobów jest odjęcie go -1.

Ogólna formuła jest więc tym, m& (-1 -(m-1)) co upraszczam&(-m)

Przykład:

          100   01100100
100-1=     99   01100011
-1-99=   -100   10011100
100&-100=   4   00000100

Oto kod: nowe linie są liczone, wcięcia są niepotrzebne i dlatego nie są liczone, jak moja inna odpowiedź. Jest nieco dłuższy niż moja inna odpowiedź z powodu niezdarnej konwersji z bazy 2: 1 2 1 4 1 2 1 etcna bazę 3: 1 3 1 9 1 3 1 etc(czy istnieje sposób, aby tego uniknąć Math::?)

def s(n)
  a=[]
  t=0
  1.upto(2*2**n-1){|m|i=3**Math::log(m&-m,2)
    a.unshift" "*t+"x"*i 
    t+=i}
  puts a
end

3

Ruby, 140 99

Mój drugi w historii kod Ruby i moje pierwsze nietrywialne użycie tego języka. Sugestie są mile widziane. Liczba bajtów wyklucza spacje wiodące dla wcięć, ale obejmuje znaki nowej linii (wydaje się, że większości nowych linii nie można usunąć, chyba że zostaną one zastąpione co najmniej spacją).

Dane wejściowe są wywoływane przez funkcję. Dane wyjściowe to tablica ciągów, które Ruby wygodnie zrzuca na standardowe wyjście jako listę oddzieloną znakiem nowej linii z pojedynczym puts.

Algorytm to po prostu new iteration= previous iteration+ extra row of n**3 x's+ previous iteration. Jednak istnieje wiele kwota fair kodu wystarczy, aby uzyskać spacje w prawym wyjściowego.

def s(n)
  a=["x"]
  1.upto(n){|m|t=" "*a[0].length
    a=a.map{|i|t+" "*3**m+i}+[t+"x"*3**m]+a}
  puts a
end

Edycja: Ruby, 97

Wykorzystuje to podobne, ale inne podejście, do budowania tabeli liczbowej wszystkich liczb x wymaganych w tablicy aw sposób opisany powyżej, ale następnie do budowania tabeli ciągów znaków. Tabela ciągów jest budowana wstecz w tablicy cprzy użyciu raczej dziwnie nazwanej unshiftmetody, aby dołączyć do istniejącej tablicy.

Obecnie to podejście wygląda lepiej - ale tylko o 2 bajty :-)

def s(n)
  a=c=[]
  (n+1).times{|m|a=a+[3**m]+a}
  t=0
  a.each{|i|c.unshift" "*t+"x"*i
    t+=i}
  puts c
end

1
Można wymienić for m in(0..n-1)do ... endz n.times{|m|...}.
Omar

@Omar Dzięki, spróbuję tego jutro. Nie uwierzyłbyś, ile wysiłku wymagało uruchomienie go z powodu ciągłych błędów składniowych. Nie wiedziałem, jak uzyskać dostęp do zmiennej iteracyjnej n.timesi na pewno to zapamiętam. To też eliminuje end! Jednak przy tej okazji zastanawiałem się, czy for m in (1..n)może być lepiej, aby uniknąć (m+1). Czy istnieje krótszy sposób napisania tego?
Level River St

1
forjest długi, głównie dlatego, że jesteś zmuszony do używania end(możesz zastąpić donowym znakiem lub znakiem ;). Do 1..nmożesz użyć 1.upto(n){|m|...}. Podoba mi się wygląd, (1..n).each{|i|...}ale jest nieco dłuższy niż używanie upto. I zauważ, że iteracja przez dzwonienie eachlub uptonie jest po prostu krótsza, to także rozważ bardziej idiomatyczny Ruby.
Omar,

@ Dzięki jeszcze raz, 1.upto(n)to jest! Po tym, gdy zniknęło kilka niepotrzebnych nawiasów, jestem już na poziomie 120. Myślę, że poniżej 100 jest możliwe, opublikuję poprawiony kod później.
Level River St

3

Haskell, 99 znaków

d=q.((iterate((1:).(>>=(:[1]).(*3)))[1])!!)
q[]=[];q(a:r)=sum r&' '++a&'x'++'\n':q r
(&)=replicate

Funkcja to d:

λ: putStr $ d 3
                                                                x
                                                             xxx
                                                            x
                                                   xxxxxxxxx
                                                  x
                                               xxx
                                              x
                   xxxxxxxxxxxxxxxxxxxxxxxxxxx
                  x
               xxx
              x
     xxxxxxxxx
    x
 xxx
x

Wszystkie te nawiasy! Czy naprawdę nie da się obejść za mniej?
FUZxxl,

Możesz stracić bajt, zamieniając równania qi robiąc q x=xw pustym przypadku listy. Ponadto wydaje się, że nawiasy wokół iterate...[1]są niepotrzebne.
Zgarb

3

PHP - 137 bajtów

function f($n){for($a=[];$i<=$n;array_push($a,3**$i++,...$a))$r=str_repeat;foreach($a as$v){$o=$r(' ',$s).$r(x,$v)."
$o";$s+=$v;}echo$o;}

Używam tutaj tej samej sztuczki, co grc . Oto wersja bez golfa:

function staircase($n)
{
    $lengthsList = [];
    for ($i = 0; $i <= $n; ++$i) {
        array_push($lengthsList, 3 ** $i, ...$lengthsList);
    }

    $output = '';
    $cumulatedLength = 0;
    foreach ($lengthsList as $length)
    {
        $output = str_repeat(' ', $cumulatedLength) . str_repeat('x', $length) . "\n" . $output;
        $cumulatedLength += $length;
    }

    echo $output;
}

3**$i-> wygląda jak PHP 5.6. Powinieneś to określić. Jest to niezgodne z prawie każdą instalacją PHP. Aby zaoszczędzić kilka bajtów, powinieneś zacząć od, $r=str_repeat;a gdzie masz tę funkcję, możesz ją zastąpić $r, oszczędzając ci 2 bajty. Ponadto $r('x',$v)może być $r(x,$v)i będzie działać dobrze (zauważ, że nazwa funkcji została już zmieniona na zmienną). Ponadto uważam, że ++$i<=$nmożna to przepisać jako $n>++$ioszczędność kolejnego bajtu.
Ismael Miguel

Oto twoja funkcja, z małą fajną sztuczką: function f($n){$r=str_repeat;$a=[1];while($n>++$i)$a=array_merge($a,[3**$i],$a);foreach($a as$v){$o=$r(' ',$s).$r(x,$v)."\r$o";$s+=$v;}echo$o;}(zamiast tej brzydkiej nowej linii, dodałem sekwencję zmiany znaczenia \rw ciągu podwójnego cudzysłowu, ze zmienną w $ośrodku. Zatem "\r$o"ma taką samą liczbę bajtów jak ta ''.$o, z ostatnią linią poleconą w ostatnim i daje ten sam wynik
Ismael Miguel

W rzeczywistości warunkiem takiej redukcji whilemusi być $n>$i++poprawne działanie.
Ismael Miguel

@ IsmaelMiguel PHP 5.6 to ostatnia wersja PHP, nie muszę nic więcej mówić. To nie moja wina, że ​​prawie wszyscy używają starej wersji, a większość korzysta z przestarzałej. Dzięki za $r=str_repeatpodstęp. Myślałem tylko o tym $r='str_repeat';, co nie oszczędzało żadnego bajtu. Nieokreślona stała to także dobra sztuczka, dobrze zrobiona;). Nowy wiersz jest o jeden bajt mniejszy niż pisanie \n, więc go zachowałem, ale użyłem podwójnych cudzysłowów, aby uniknąć konkatenacji $0. Dzięki jeszcze raz !
Blackhole

Dobrze by na ciebie wyglądało. Gdybym nie był tego świadomy 3 ** $i, powiedziałbym, że masz straszną składnię. Możesz rozwiązać tę poprawkę. Mówię tylko o tym, a nie [1]dlatego, że pochodzi on z PHP5.4, który jest dość „stary”. 1 rok temu poprosiłbym cię o określenie tego. Dzisiaj proszę was o proste określenie (w bardzo krótkim wierszu), które to określa. Mówiąc o kodzie, nadal ++$i<=$nmożesz go zastąpić $n>$i++. Musiałem przekonwertować cały kod na PHP5.3, aby go przetestować. Co było bolesne. Ale widzę, że do tej pory zjadłeś 7 bajtów.
Ismael Miguel

3

C, 165

#define W while
f(n){int i=n+1,j=1<<i,k=1,l,r,s,t;W(i--)k*=3;l=k-j;W(--j){r=j,s=1;W(!(r%2))r/=2,s*=3;l-=s;t=l;W(t--)putchar(32);W(++t<s)putchar(88);putchar('\n');}}

Oto ten sam kod rozpakowany i lekko wyczyszczony:

int f(int n) {
    int i=n+1, j=1<<i, k=1;
    while (i--) k*=3;
    int l=k-j;
    while (--j) {
        int r=j,s=1;
        while (!(r%2))
            r/=2, s*=3;
        l-=s;
        int t=l;
        while (t--) putchar(' ');
        while (++t<s) putchar('X');
        putchar('\n');
    }
}

Jest to oparte na tym samym pomyśle, co rozwiązanie FUZxxl, polegające na użyciu jawnej, a nie domyślnej formy wierszy. Deklaracja j ustawia ją na 2 ^ (n + 1), a pierwsza pętla while oblicza k = 3 ^ (n + 1); wtedy l = 3 ^ (n + 1) -2 ^ (n + 1) to całkowita szerokość schodów (nie jest to zbyt trudne do udowodnienia). Następnie przechodzimy przez wszystkie liczby r od 1 do 2 ^ (n + 1) -1; dla każdego, jeśli można podzielić przez (dokładnie) 2 ^ n, to planujemy wydrukować s = 3 ^ n 'X. l jest dostosowywany, aby mieć pewność, że zaczniemy od właściwego miejsca: piszemy l spacje i litery X, a następnie nową linię.


zdefiniuj W do; while i pomiń int, aby zapisać niektóre znaki.
FUZxxl

także t = l- = s dla pewnego oszczędzania.
FUZxxl

@FUZxxl Próbowałem obu z nich, ale chociaż C nadal dopuszcza typy niejawne w funkcjach, nie pozwalało im na deklaracje zmiennych nawet przy flagach „klasycznych” (przynajmniej na GCC). I próbowałem #define W; podczas gdy i nie wyglądało na to, żeby to obchodziło, chociaż być może wpadłem w błąd w definicji.
Steven Stadnicki

hm ... Myślę, że można pominąć typ tylko w zmiennej globalnej. Nie przynosi ci to zbyt wiele. Możesz spróbować dodać (*p)()=putchar;na początku, aby zadzwonić putcharjako p. Myślę, że to powinno działać.
FUZxxl,

2

CJam, 46 43 41 39 36 35 bajtów

L0ri),(a*+_W%(;+{3\#'x*+_,S*}%$1>N*

AKTUALIZUJ, używając teraz innego podejścia.


Stare podejście:

]ri){3f*_,)"x"a*\]z:+}*_s,f{1$,U+:U-S*\N}

Dość naiwny i długi, ale coś na początek.

Dodam wyjaśnienie, gdy zacznę grać w golfa.

Wypróbuj online tutaj


Wydaje się, że potrzebuję trochę pracy. Nie działał poprawnie dla n = 4, 5, 17. Wyświetlał sformatowane w lewo ciągi riffów x w górnej części. Przy n = 17 zrzucił kod na ekran i wypełnił dół znakami x.
DavidC

1
@DavidCarraher Dla 4, 5 Myślę, że to tylko zawijanie linii. Jeśli skopiujesz dane wyjściowe do edytora tekstowego bez zawijania linii, dla mnie będzie to w porządku.
Sp3000,

Dobrze. Wiem, widzę.
DavidC

2

Java, 271 269 ​​bajtów

Używa metody grc.

import java.util.*;String a(int a){List<Integer>b=new ArrayList<>();int c=-1,d=1;for(;c++<a;b.add(d),b.addAll(b),b.remove(b.size()-1),d*=3);String f="";for(;b.size()>0;f+="\n"){d=b.remove(b.size()-1);for(int g:b)for(c=0;c<g;c++)f+=' ';for(c=0;c<d;c++)f+='x';}return f;}

Zębaty:

import java.util.*;
String a(int a){
    List<Integer>b=new ArrayList<>();
    int c=-1,d=1;
    for(;c++<a;b.add(d),b.addAll(b),b.remove(b.size()-1),d*=3);
    String f="";
    for(;b.size()>0;f+="\n"){
        d=b.remove(b.size()-1);
        for(int g:b)
            for(c=0;c<g;c++)
                f+=' ';
        for(c=0;c<d;c++)
            f+='x';
    }
    return f;
}

Wszelkie sugestie są mile widziane.

2 bajty dzięki mbomb007


Możesz użyć b.size()>0zamiast !b.isEmpty(), oszczędzając 2 bajty.
mbomb007

1

Perl, 62

#!perl -p
eval's/x+/$&$&$&
x/g,s/\d*/x
/;'x++$_;s/x+/$"x$'=~y!x!!.$&/ge

Najpierw oblicza wynik iteracyjnie bez spacji wiodących. Następnie dodaje je przed każdym wierszem zgodnie z liczbą xznaków w pozostałej części ciągu.


1

JavaScript (ES6) 104 106 118

Edytuj Usunięto funkcję rekurencyjną, listę „*” dla każdej linii uzyskuje się iteracyjnie, bawiąc się bitami i potęgami 3 (jak w wielu innych odpowiedziach)
Wewnątrz pętli od dołu do góry jest budowany ciąg wielowierszowy, utrzymujący bieżącą liczbę wiodących spacji do dodania w każdej linii

F=n=>{
  for(i=a=s='';++i<2<<n;a=s+'*'.repeat(t)+'\n'+a,s+=' '.repeat(t))
    for(t=u=1;~i&u;u*=2)t*=3;
  return a
}

Pierwsza próba została usunięta

Rekurencyjna funkcja R buduje tablicę z liczbą „*” dla każdej linii. Na przykład R (2) to [1, 3, 1, 9, 1, 3, 1]
Ta tablica jest skanowana w celu zbudowania ciągu wielowierszowego od dołu do góry, zachowując bieżącą liczbę wiodących spacji do dodania w każdej linii

F=n=>
(R=n=>[1].concat(...n?R(n-1).map(n=>[n*3,1]):[]))(n)
.map(n=>a=' '.repeat(s,s-=-n)+'*'.repeat(n)+'\n'+a,a=s='')
&&a 

Przetestuj w konsoli Firefox / FireBug

F(3)

Wynik

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

1

R - 111 znaków

Prosta implementacja, iteracyjne budowanie tablicy i niszczenie jej powoli.

n=scan()
a=1
if(n)for(x in 1:n)a=c(a,3^x,a)
for(A in a){cat(rep(' ',sum(a)-A),rep('x',A),'\n',sep='');a=a[-1]}

Stosowanie:

> source('devil.r')
1: 2
2: 
Read 1 item
                  x
               xxx
              x
     xxxxxxxxx
    x
 xxx
x

Dobra uwaga, zmodyfikowałem mój kod, aby n
pobierał

1
Zapisujesz 8 bajtów, czytając ze STDIN. n=scan().
Alex A.,

Nie musisz deklarować xużywania go jako kursora, ani nie potrzebujesz if(n). Również łamanie linii liczy się jako znak.
freekvd

Dzięki, masz rację x. Nie jestem if(n)jednak pewien . Dodałem tę część, aby zająć się sprawą n=0. if(n)Następnie powraca Fi tym samym zwraca pojedynczy x. Jeśli go n=0usunę , daje niechciane wyniki. Nowość tutaj, więc nie wiedziałem o łamaniu linii. Uwzględnione teraz!
koekenbakker

Jeśli ustawisz a=0i uruchomisz pętlę x in 0:n, działa to również dla n = 0. Następnie możesz pominąć if(n).
freekvd,

0

Ruby, 93

f=->n{s,p,k=[1],1;n.times{s=s+[p=p*3]+s};k=s.dup;k.each{m=s.pop;puts' '*s.reduce(0,:+)+?x*m}}

Używa tego samego podejścia co grc.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.