Cicli sul toro


20

Sfida

Questa sfida ti farà scrivere un programma che accetta due numeri interi ne mche genera il numero di loop non intersecanti sul nby mtoro fatto partendo da (0,0)e solo facendo passi su e verso destra. Puoi pensare al toro come alla griglia con avvolgente sia in alto che in basso e ai lati.

Si tratta di quindi vince meno byte.

Esempio

Ad esempio, se l'ingresso è n=m=5, una camminata valida è

(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)

come mostrato nel grafico.

Un anello sul toro.

Alcuni esempi di input / output

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
Ero disposto a scommettere che questa sequenza era su OEIS per almeno , ma apparentemente non lo è (ancora). m=n
Arnauld

Penso che un toro abbia anche un avvolgimento sinistro-destro. Dovremmo supporre che abbia invece solo avvolgente up-down? L'immagine di esempio non sembra implicare come tale.
Erik the Outgolfer

@EriktheOutgolfer L'immagine mostra il percorso arancione che gira da destra a sinistra, no?
Arnauld

@Arnauld Sì, ma non sembra coerente con la descrizione della sfida ("Puoi pensare al toro come alla griglia con avvolgente sia in alto che in basso")
Erik the Outgolfer

@EriktheOutgolfer È vero. E ora che lo dici, il percorso blu è sbagliato. Dovrebbe prima avvolgersi da destra a sinistra e poi dall'alto verso il basso.
Arnauld

Risposte:


4

Gelatina , 28 byte

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

Un collegamento monadico che accetta un elenco [m,n], che fornisce il conteggio.

TIO-jt1qe1v9 ... anche se c'è poco punto, è troppo inefficiente.
(Non riesco nemmeno a correre[2,3]localmente con ram da 16 GB)!

Come?

Forza bruta: crea le coordinate di una versione piastrellata abbastanza grande, quindi filtra il gruppo di potenza di questi punti su quei percorsi con i vicini che aumentano solo di uno in una sola direzione, quindi filtra su quelli che iniziano con una coordinata minima (cioè l'origine) e, allo stesso tempo, rimuove questa coordinata iniziale da ciascuna. Quindi utilizza l'aritmetica del modulo per tornare a un toro e filtra tutte le coordinate duplicate (cioè quelle che contengono intersezioni) e, infine, filtra a quelle con coordinate finali minime (ovvero che termina all'origine) e produce la lunghezza del risultato.

ạƝ§=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 byte

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

Provalo online!

La cosa interessante qui è usare un numero complesso zper memorizzare le coordinate della posizione corrente. Possiamo salire aggiungendo1 e spostandoci a destra aggiungendo 1j. Con mia sorpresa, modulo lavora su numeri complessi in un modo che ci consente di gestire il wrapping per ogni dimensione separatamente: facendo %matti sulla parte reale e %(n*1j)agendo sulla parte immaginaria.


Ben fatto. FWIW, il mio miglior tentativo senza usare un numero complesso è di 91 byte in Python 3.8.
Arnauld

@Arnauld Idea interessante con il k:=x+y*m. Mi chiedo se sarebbe più breve usare kdirettamente (x,y), usando x+y*mpiuttosto che x+y*1j. Peccato che Python 3 non consenta un modulo complesso.
xnor


Questo approccio consente di risparmiare 5 byte in JS. :)
Arnauld

7

JavaScript (ES6), 67 byte

m×n<32 in JS.

Accetta input come (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))``

Provalo online!

Per farlo funzionare per qualsiasi input, potremmo usare BigInts per 73 byte :

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)

Provalo online!


JavaScript (ES6),  76 73  72 byte

Accetta input come (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)

Provalo online!

Commentate

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 byte

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$[]

Provalo online!

Forza bruta semplice: prova tutte le combinazioni su / destra, lasciando cadere quelle che si intersecano (manteniamo nell'elenco tutte le posizioni che abbiamo visitato a ) e contando quelle che alla fine colpiscono di (0,0)nuovo la posizione.

Il caso base della ricorsione è quando visitiamo una posizione una seconda volta ( elem(x,y)a). Il risultato è 0^0= 1quando la posizione è (0,0)e conta verso il numero di loop o 0( 0^x, conx diverso da zero) altrimenti e non aumenta il numero di loop.

Modifica: -8 byte grazie a @xnor.


1
I casi di base possono essere combinati |elem(x,y)a=0^(x+y)e (0!0)[]possono esserlo 0!0$[].
xnor


1

Java 8, 120 byte

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

n*m<32

Provalo online.


1

CJam (50 caratteri)

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

Demo online . Questo è un programma che accetta due input da stdin.

Finalmente abbiamo una risposta alla domanda

Guerra, eh, a cosa serve?


Dissezione

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

Gelatina , 54 39 byte

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

Provalo online!

Ho pubblicato questo come una risposta separata all'altra mia Jelly perché è un metodo completamente diverso. Questo è più vicino in linea di principio alla risposta di @ Arnauld. Utilizza una funzione ricorsiva che lavora attraverso ogni possibile percorso fino a quando non raggiunge un punto a cui è già arrivato, quindi restituisce il risultato di un controllo se è tornato all'inizio. Sospetto che qualche byte in più potrebbe essere eliminato. Ora cambiato con l'utilizzo dell'operatore slice. Funziona bene fino a 5x5. La profondità di ricorsione dovrebbe essere al massimo mx n.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.