Cykle na torusie


20

Wyzwanie

Wyzwanie to będzie można napisać program, który odbywa się w dwóch liczb całkowitych na mi wysyła liczbę pętli niekrzyżujące sprawie nprzez mtorus dokonanych przez zaczynając (0,0)a jedynie podjęcie kroków w górę i w prawo. Możesz myśleć o torusie jak o siatce z zawijaniem u góry iu dołu oraz po bokach.

To jest więc wygrywa najmniej bajtów.

Przykład

Na przykład, jeśli dane wejściowe to n=m=5, jeden prawidłowy spacer to

(0,0) -> (0,1) -> (0,2) -> (1,2) -> (2,2) -> (2,3) -> (2,4) -> 
(2,0) -> (3,0) -> (4,0) -> (4,1) -> (4,2) -> (4,3) -> 
(0,3) -> (1,3) -> (1,4) -> 
(1,0) -> (1,1) -> (2,1) -> (3,1) -> (3,2) -> (3,3) -> (3,4) -> (4,4) -> 
(0,4) -> (0,0)

jak pokazano na grafice.

Pętla na torusie.

Niektóre przykładowe wejścia / wyjścia

f(1,1) = 2 (up or right)
f(1,2) = 2 (up or right-right)
f(2,2) = 4 (up-up, up-right-up-right, right-right, right-up-right-up)
f(2,3) = 7
f(3,3) = 22
f(2,4) = 13
f(3,4) = 66
f(4,4) = 258

1
m=n

Myślę, że torus ma również lewe i prawe owinięcie. Czy powinniśmy założyć, że zamiast tego ma tylko zawijanie góra-dół? Przykładowy obraz nie wydaje się sugerować jako taki.
Erik the Outgolfer

@EriktheOutgolfer Obraz pokazuje pomarańczową ścieżkę otaczającą od prawej do lewej, prawda?
Arnauld

@Arnauld Tak, ale nie wygląda spójnie z opisem wyzwania („Możesz myśleć o torusie jako o siatce z zawijaniem zarówno na górze, jak i na dole.”)
Erik Outgolfer

@EriktheOutgolfer To prawda. A teraz, kiedy o tym wspominasz, niebieska ścieżka jest zła. Powinien najpierw owinąć się od prawej do lewej, a następnie od góry do dołu.
Arnauld

Odpowiedzi:


4

Galaretka , 28 bajtów

ạƝ§=1Ȧ
²‘p/’ŒPÇƇḢÐṂ%⁸QƑƇṪÐṂL

Monadyczny link akceptujący listę [m,n], co daje wynik.

TIO-jt1qe1v9 ... chociaż nie ma to większego sensu, jest to zbyt mało wydajne.
(Nie mogę nawet uruchomić[2,3]lokalnie z 16 GB RAM)!

W jaki sposób?

Brute force - tworzy współrzędne wersji kafelkowej wystarczająco duże, następnie filtruje zestaw mocy tych punktów do tych ścieżek, w których sąsiedzi zwiększają się tylko o jeden w jednym kierunku, a następnie filtruje do tych zaczynających się od minimalnej współrzędnej (tj. Początku) i, jednocześnie usuwa tę współrzędną początkową z każdego. Następnie wykorzystuje arytmetykę modulo, aby zawinąć z powrotem do torusa i odfiltrowuje wszelkie zawierające zduplikowane współrzędne (tj. Zawierające przecięcia), a na koniec filtruje do tych o minimalnych współrzędnych końcowych (tj. Kończących się na początku) i zwraca długość wyniku.

ạƝ§=1Ȧ - Link 1: all neighbours differ by 1 in exactly one direction
 Ɲ     - for neighbours:
ạ      -   absolute difference
  §    - sum each
   =1  - equal to one (vectorises)
     Ȧ - any and all? (falsey if empty or contains a falsey value when flattened)

²‘p/’ŒPÇƇḢÐṂ%⁸QƑƇṪÐṂL - Main Link: list of integers, [m,n]
²                     - square (vectorises) -> [m*m, n*n]
 ‘                    - increment (vectorises) -> [m*m+1, n*n+1]
   /                  - reduce with:
  p                   -   Cartesian product
    ’                 - decrement (vectorises) -> all the coordinates of an m*m by n*n grid
                      -                           including [0, 0] and [m*m, n*n] 
     ŒP               - power-set -> all paths going either up OR right at each step, but not
                      -              necessarily by only 1, and
                      -              necessarily both up and right (e.g. [...[1,3],[5,7],[6,2],...])
        Ƈ             - filter keep those for which:
       Ç              -   call last Link (1) as a monad
                      -              ...now all remaining paths do only go in steps
                      -              of one up or one right
          ÐṂ          - filter keep those minimal under:
         Ḣ            -   head - removes the 1st coordinate from each and yields them for the filter
                      -          ...so only those which started at [0,0] but without it
            %⁸        - modulo by the left argument ([m,n]) (vectorises)
                Ƈ     - filter keep those for which:
               Ƒ      -   is invariant when:
              Q       -     de-duplicated
                      -          ...so no repetitions of torus coordinates (and we already removed
                      -          the first [0,0] which must be present exactly twice)
                  ÐṂ  - filter keep those minimal under:
                 Ṫ    -   tail
                      -          ...so only those which ended at [0,0] 
                    L - length

12

Python 2 , 87 bajtów

f=lambda m,n,z=0,l=[]:z==0if z in l else sum(f(m,n,(z+d)%m%(n*1j),l+[z])for d in(1,1j))

Wypróbuj online!

Interesującą rzeczą jest tutaj użycie liczby zespolonej zdo przechowywania współrzędnych aktualnej pozycji. Możemy przejść w górę, dodając 1i przejść w prawo, dodając 1j. Ku mojemu zaskoczeniu, modulo działa na liczbach zespolonych w sposób, który pozwala nam osobno obsługiwać zawijanie dla każdego wymiaru: wykonując %mczynności na części rzeczywistej i %(n*1j)działając na części urojonej.


Ładnie wykonane. FWIW, moja najlepsza próba bez użycia liczby zespolonej to 91 bajtów w Pythonie 3.8.
Arnauld

@Arnauld Ciekawy pomysł z k:=x+y*m. Zastanawiam się, czy byłoby to krótsze użycie kbezpośrednio (x,y), x+y*mzamiast używać x+y*1j. Szkoda, że ​​Python 3 nie pozwala na skomplikowany moduł.
xnor


Takie podejście pozwala zaoszczędzić 5 bajtów w JS. :)
Arnauld

7

JavaScript (ES6), 67 bajtów

m×n<32

Pobiera dane wejściowe jako (m)(n).

m=>n=>(g=(k,l)=>l>>k&1?!k:g((k+m)%(m*n),l|=1<<k)+g(k-~k%m-k%m,l))``

Wypróbuj online!

Aby działało dla dowolnego wejścia, moglibyśmy użyć BigInts dla 73 bajtów :

m=>n=>(g=(k,l=k)=>l&(b=1n<<k)?!k:g((k+m)%(m*n),l|=b)+g(k-~k%m-k%m,l))(0n)

Wypróbuj online!


JavaScript (ES6),  76 73  72 bajty

Pobiera dane wejściowe jako (m)(n).

m=>n=>(g=(x,y)=>g[x+=y*m]?!x:g(-~x%m,y,g[x]=1)+g(x%m,-~y%n)+--g[x])(0,0)

Wypróbuj online!

Skomentował

m => n => (         // m = width; n = height
  g = (             // g is a recursive function taking:
        x, y        //   the current coordinates (x, y) on the torus
      ) =>          //
    g[              // the surrounding object of g is also used for storage
      x += y * m    // turn x into a key for the current coordinates
    ] ?             // if this cell was already visited:
      !x            //   return 1 if we're back to (0, 0), or 0 otherwise
    :               // else:
      g(            //   first recursive call:
        -~x % m,    //     move to the right
        y,          //     leave y unchanged
        g[x] = 1    //     mark the current cell as visited by setting the flag g[x]
      ) +           //   add the result of
      g(            //   a second recursive call:
        x % m,      //     restore x in [0...m-1]
        -~y % n     //     move up
      ) +           //
      --g[x]        //   clear the flag on the current cell
)(0, 0)             // initial call to g with (x, y) = (0, 0)

3

Haskell, 88 80 bajtów

n#m|let(x!y)a|elem(x,y)a=0^(x+y)|b<-(x,y):a=(mod(x+1)n!y)b+(x!mod(y+1)m)b=0!0$[]

Wypróbuj online!

Prosta brutalna siła: wypróbuj wszystkie kombinacje góra / prawo, upuszczając te, które się przecinają (utrzymujemy wszystkie pozycje, które odwiedziliśmy na liście a) i licząc te, które ostatecznie osiągną pozycję (0,0).

Podstawowy przypadek rekurencji ma miejsce, gdy odwiedzamy pozycję po raz drugi ( elem(x,y)a). Wynikiem jest 0^0= 1kiedy pozycja jest (0,0)i liczy się do liczby pętli lub 0( 0^xz xniezerową) w przeciwnym razie i nie zwiększa liczby pętli.

Edycja: -8 bajtów dzięki @xnor.


1
Przypadki podstawowe można łączyć |elem(x,y)a=0^(x+y)i (0!0)[]można je łączyć 0!0$[].
xnor


1

Java 8, 120 bajtów

n->m->g(n,m,0,0);int g(int n,int m,int k,int l){return(l>>k)%2>0?k<1?1:0:g(n,m,(k+m)%(m*n),l|=1<<k)+g(n,m,k-~k%m-k%m,l);}

nm<32

Wypróbuj online.


1

CJam (50 znaków)

q~]:M:!a{9Yb2/\f{_W=@.+M.%a+_)a#g"WAR"=~}}:R~e_We=

Demo online . Jest to program, który pobiera dwa wejścia ze standardowego wejścia.

Wreszcie mamy odpowiedź na pytanie

Wojna, po co to jest dobre?


Sekcja

q~]:M        e# Parse input, collect in array, store in M (for moduli)
:!a          e# Zero and wrap in array for starting position (0, 0)
{            e# Define recursive block R
  9Yb2/      e#   Push [[1 0][0 1]], an array of movements
  \f{        e#   For each of those movements, with the current path,
    _W=@.+   e#     Add the movement to the last position in the path
    M.%      e#     Apply the wrapping
    a+       e#     Add to one copy of the path
    _)a#     e#     And find its index in another copy
    g"WAR"=~ e#     Switch on the sign of the index:
             e#       If the sign is -1, position not found, make a recursive call
             e#       If the sign is 0, found at start, push -1 to the stack
             e#       If the sign is 1, we have a self-intersection. We push 10 to
             e#       the stack for no other reason than to make the bad joke above
  }
}:R
~            e# Execute R
e_We=        e# Count the -1s which we pushed as sentinels

1

Galaretka , 54 39 bajtów

ḣ2æ.2ị³¤+4
‘Ç;¥¦%³Ç=4ƊÑÇị$?
çⱮؽS
’Ñ0xÇ

Wypróbuj online!

Opublikowałem to jako osobną odpowiedź na moją drugą galaretkę, ponieważ jest to zupełnie inna metoda. Jest to zasadniczo bliższe odpowiedzi @ Arnauld. Używa funkcji rekurencyjnej, która działa przez każdą możliwą ścieżkę, aż osiągnie punkt, do którego już dotarła, a następnie zwraca wynik sprawdzenia, czy jest z powrotem na początku. Podejrzewam, że kilka innych bajtów można by ogolić. Teraz zmieniono na używanie operatora wycinania. Działa dobrze do 5x5. Głębokość rekurencji powinna wynosić co najwyżej mx n.

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.