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
Peter Kagey
źródło
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
Jonathan Allan
źródło
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.

xnor
źródło
Ł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)
Arnauld
źródło
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.

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

Galaretka , 44 bajty

×ƝṪ2*Ḥ_2Rḃ€2ċⱮؽ%³¬ẠƲƇịØ.Ṛ,Ø.¤ZÄZ%€ʋ€³ŒQẠ$€S

Wypróbuj online!

O(2)mn)

mn

Nick Kennedy
źródło
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.

Kevin Cruijssen
źródło
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
Peter Taylor
źródło
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.

Nick Kennedy
źródło