Циклы на торе

20

Вызов

Эта задача будет вам написать программу , которая принимает в двух целых чисел nи mи выводит число непересекающихся петель на nна mторе , сделанных начиная с (0,0)и только предпринимают шаги вверх и вправо. Вы можете думать о торе как о решетке с закруглением сверху и снизу и по бокам.

Это поэтому побеждает меньше байтов.

пример

Например, если введено n=m=5одно допустимое

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

как показано на рисунке.

Петля на торе.

Некоторые примеры ввода / вывода

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
Я был готов поспорить, что эта последовательность была на OEIS, по крайней мере, , но, видимо, это не так (пока). мзнак равноN
Арно
Я думаю, что у тора также есть обтекание влево-вправо. Должны ли мы предполагать, что он имеет только восходящий-обратный виток? Изображение примера, похоже, не подразумевает таковое.
Эрик Outgolfer
@EriktheOutgolfer На изображении показан оранжевый путь, оборачивающийся справа налево, не так ли?
Арно
@Arnauld Да, но это не выглядит в соответствии с описанием задачи («Вы можете думать о торе как о решетке с закруглением как сверху, так и снизу.»)
Эрик Аутгольфер
@EriktheOutgolfer Это правда. И теперь, когда вы упомянули об этом, синий путь неправильный. Сначала следует обернуть справа налево, а затем сверху вниз.
Арно

Ответы:

4

Желе , 28 байт

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

Монадическая ссылка, принимающая список [m,n], который выдает счет.

TIO-jt1qe1v9 ... хотя в этом нет особого смысла, он слишком неэффективен.
(Я даже не могу работать[2,3]локально с 16 ГБ оперативной памяти)!

Как?

Грубая сила - создает достаточно большие координаты мозаичной версии, затем фильтрует набор мощностей этих точек по тем путям, у которых соседние объекты увеличиваются только на единицу в одном направлении, затем фильтрует по тем, которые начинаются с минимальной координаты (т. Е. Начала координат), и, в то же время удаляет эту начальную координату из каждого. Затем использует арифметику по модулю для возврата к тору и отфильтровывает любые содержащие повторяющиеся координаты (т. Е. Содержащие пересечения) и, наконец, фильтрует те, которые имеют минимальные конечные координаты (т. Е. Заканчивающиеся в начале координат), и возвращает длину результата.

ạƝ§=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 байт

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

Попробуйте онлайн!

Здесь интересно использовать комплексное число zдля хранения координаты текущей позиции. Мы можем двигаться вверх, добавляя, 1и двигаться вправо, добавляя 1j. К моему удивлению, модуль работает с комплексными числами таким образом, что позволяет нам обрабатывать перенос для каждого измерения отдельно: выполнение %mдействует на действительную часть и %(n*1j)воздействует на мнимую часть.

XNOR
источник
Красиво сделано. FWIW, моя лучшая попытка без использования комплексного числа - 91 байт в Python 3.8.
Арно
@Arnauld Интересная идея с k:=x+y*m. Это заставляет меня задуматься о том, будет ли короче использовать kнепосредственно для (x,y), используя, x+y*mа не x+y*1j. Жаль, что Python 3 не допускает сложный модуль.
XNOR
Этот подход экономит 5 байтов в JS. :)
Арно
7

JavaScript (ES6), 67 байт

м×N<32

Принимает вход как (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))``

Попробуйте онлайн!

Чтобы это работало для любого ввода, мы могли бы использовать BigInts для 73 байтов :

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)

Попробуйте онлайн!


JavaScript (ES6),  76 73  72 байта

Принимает вход как (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)

Попробуйте онлайн!

комментарии

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
источник
3

Haskell, 88 80 байт

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

Попробуйте онлайн!

Простая грубая сила: попробуйте все комбинации вверх / вправо, отбрасывая те, которые пересекаются (мы сохраняем все позиции, которые мы посетили, в списке a) и подсчитываем те, которые в конечном итоге достигают позиции (0,0)снова.

Базовый случай рекурсии - это когда мы посещаем позицию во второй раз ( elem(x,y)a). Результатом является 0^0=, 1когда позиция равна (0,0)и учитывает количество циклов или 0( 0^xс xненулевым значением) в противном случае и не увеличивает количество циклов.

Изменить: -8 байт благодаря @xnor.

Ними
источник
1
Базовые случаи могут быть объединены в |elem(x,y)a=0^(x+y)и (0!0)[]могут быть 0!0$[].
xnor
1

Java 8, 120 байт

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*м<32

Попробуйте онлайн.

Кевин Круйссен
источник
1

CJam (50 символов)

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

Демо онлайн . Это программа, которая принимает два входа от стандартного ввода.

Наконец у нас есть ответ на вопрос

Война, а для чего это хорошо?


рассечение

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

Желе , 54 39 байт

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

Попробуйте онлайн!

Я отправил это как отдельный ответ на мой другой Jelly, потому что это совершенно другой метод. Это в принципе ближе к ответу @ Арно. Он использует рекурсивную функцию, которая работает через все возможные пути, пока не достигнет точки, к которой уже достиг, а затем возвращает результат проверки, вернулась ли она в начало. Я подозреваю, что можно сбрить еще несколько байтов. Теперь изменилось использование оператора слайса. Хорошо работает до 5х5. Глубина рекурсии должна быть не более mx n.

Ник Кеннеди
источник