Menghitung jumlah hutan terlarang pada tangga Möbius dengan panjang n


13

Urutan OEIS A020872 menghitung jumlah hutan terlarang pada tangga Möbius M n .

Tantangan

Tantangannya adalah untuk menulis sebuah program yang mengambil bilangan bulat sebagai input n > 1dan mengembalikan A020872(n), jumlah hutan terbatas pada tangga Möbius M n . Ini , jadi kode terpendek menang. (Motif tersembunyi mungkin untuk sedikit memperpanjang urutan ini.)

Definisi

Sebuah hutan terbatas adalah partisi dari grafik tersebut bahwa setiap bagian adalah baik (tidak diarahkan) path atau vertex terisolasi.

The Möbius tangga M n adalah grafik yang dapat dianggap 2n-gon dengan diagonal ditarik antara semua simpul sebaliknya.

Contoh

Berikut adalah 34 hutan terbatas pada M 2 (persegi dengan diagonal digambar). Perhatikan bahwa grafik pertama dipartisi menjadi empat simpul yang terisolasi, yang kedua dipartisi menjadi satu jalur dan dua simpul yang terisolasi, dll. A020872 (2)


1
Uji kasus dari 2 sampai 12: 34, 241, 1582, 10204, 65197, 415076, 2638366, 16759249, 106427154, 675771276, 4290678337. Saya tidak yakin mengapa input 1juga tidak diperlukan, dengan output 2.
Peter Taylor

@PeterTaylor, Terima kasih telah menambahkan istilah itu ke OEIS! Saya mengecualikan input 1karena M_1 tidak didefinisikan secara jelas dalam artikel Wikipedia. (Khususnya, memiliki beberapa sisi atau bukan grafik kubik.)
Peter Kagey

1
Ini sebenarnya terdengar seperti kandidat yang baik untuk tantangan fastest-codeatau fastest-algorithmtantangan.
mypetlion

1
Kasus uji lebih lanjut ( kode pembangkitan ): 13 hingga 17 adalah27242281044, 172964658642, 1098170541121, 6972388689086, 44268329738124
Peter Taylor

1
Benar, saya pikir motif tersembunyi Anda lebih dari puas.
Peter Taylor

Jawaban:


10

CJam ( 58 56 karakter)

Beberapa karakter tidak dapat dicetak, dan satu adalah tab yang akan dikacaukan oleh perangkat lunak StackExchange:

"¶3¬î¿Á·    7ÛÈmÈÚÚ¡"256b454b212f-{__W%.*A<1b+}qi*-4=

Demo online . Ini akan berjalan online untuk n = 400 dalam waktu sekitar tiga detik.

Dikodekan oleh xxd:

0000000: 22b6 0233 93ac eebf c1b7 0609 3794 dbc8  "..3........7...
0000010: 6dc8 1015 dada a122 3235 3662 3435 3462  m......"256b454b
0000020: 3231 3266 2d7b 5f5f 5725 2e2a 413c 3162  212f-{__W%.*A<1b
0000030: 2b7d 7169 2a2d 343d                      +}qi*-4=

Penjelasan

Tangga Möbius pada dasarnya adalah tangga dengan dua sisi tambahan. Mengingat hutan terbatas di tangga, dapat diangkat ke antara 1 dan 4 hutan terbatas di tangga Möbius. Tepi dapat ditambahkan asalkan tidak membuat simpul derajat 3 atau siklus. Derajat keempat sudut dan interkoneksi mereka membentuk 116 kelas hutan terbatas pada tangga, meskipun beberapa di antaranya setara karena simetri persegi panjang. Saya menulis sebuah program untuk menganalisis ekstensi dari sebuah tangga dengan panjang n ke salah satu dari panjang n + 1, dan kemudian menggabungkan kelas menjadi 26 kelas kesetaraan. Ini memberikan formulir tertutup

[1111]T[1220121123410010]n2[0100]+

[221111122]T[211111111101001010002010000001010000000100001110000011001000011322112142000100002]n2[002200000]+

[1244113222344]T[0001000000100020010000000001201101101111004003002000000000001021001000000000111001002001000012000010001201001000000000002002001000000000000010000000000102200230110210124]n2[1011201000121]

so values can be computed fast by taking three linear recurrences and then adding them, but this isn't looking very golfy.

However, if we take the irreducible factors of the various characteristic polynomials and multiply together one of each (ignoring multiplicity) we get a polynomial of degree 10 which gives a working single linear recurrence.

Constructive approach (58 chars)

qi:Q2*,Wa*e!{Wa/{_W%e<}%$}%_&{{,1>},2few:~{:-z(Q(%}%0-!},,

Online demo. It will run online for n=2 without problems and for n=3 with a bit of patience. For n=1 it crashes, but since OP has chosen to exclude that case from the requirements it's not a fundamental problem.

Dissection

qi:Q          e# Take input from stdin, parse to int, store in Q
2*,Wa*e!      e# Take all permutations of (0, -1, 1, -1, 2, -1, ..., -1, 2*Q-1)
{             e# Map to canonical form...
  Wa/         e#   Split around the -1s
  {_W%e<}%    e#   Reverse paths where necessary to get a canonical form
  $           e#   Sort paths
}%
_&            e# Filter to distinct path sets
{             e# Filter to path sets with valid paths:
  {,1>},      e#   Ignore paths with fewer than two elements (can't be invalid; break 2ew)
  2few:~      e#   Break paths into their edges
  {:-z(Q(%}%  e#   The difference between the endpoints of an edge should be +/-1 or Q (mod 2Q)
              e#   So their absolute values should be 1, Q, 2Q-1.
              e#   d => (abs(d)-1) % (Q-1) maps those differences, and no other possible ones, to 0
              e#   NB {:-zQ(%}% to map them all to 1 would save a byte, but wouldn't work for Q=2
  0-!         e#   Test that all values obtained are 0
},
,             e# Count the filtered distinct path sets

A more efficient version takes 98 bytes:

qi2*:Q{a{__0=[1Q2/Q(]f+Qf%_&1$-\f{+E}~}:E~}/]{_W%>!},:MW=0{_{M\f{__3$_@&@:e<@|^{=}{^j}?}1b}{,)}?}j

Online demo

This builds the possible paths by depth-first search, then uses a memoised function which counts the possible restricted forests for a given set of vertices. The function works recursively on the basis that any restricted forest for a given non-empty set of vertices consists of a path containing the smallest vertex and a restricted forest covering the vertices not in that path.


On the grid graph, this can be described with a linear recursion, so it wouldn’t surprise me to find out that this is nice.
Peter Kagey

6

JavaScript (ES6),  160 158  146 bytes

n=>(g=(e,v,p)=>[...Array(N=2*n),N-1,1,n].reduce((s,x,i)=>(m=1<<(x=i<N?i:(p+x)%N))&v?s:s+g((i>=N)/p?[...e,1<<p|m]:e,v|m,x),g[e.sort()]^(g[e]=1)))``

Try it online!

Notes:

  • This is quite inefficient and will time-out on TIO for n>4.
  • a(5)=10204 was found in a bit less than 3 minutes on my laptop.

Commented

n => (                        // n = input
  g = (                       // g = recursive function taking:
    e,                        //   e[] = array holding visited edges
    v,                        //   v   = bitmask holding visited vertices
    p                         //   p   = previous vertex
  ) =>                        // we iterate over an array of N + 3 entries, where N = 2n:
    [ ...Array(N = 2 * n),    //   - 0...N-1: each vertex of the N-gon (starting points)
      N - 1,                  //   - N      : previous vertex \
      1,                      //   - N+1    : next vertex      }-- connected to p
      n                       //   - N+2    : opposite vertex /
    ].reduce((s, x, i) =>     // reduce() loop with s = accumulator, x = vertex, i = index:
      ( m = 1 << (            //   m is a bitmask where only the x-th bit is set
          x = i < N           //   and x is either:
              ? i             //   - i if i < N
              : (p + x) % N   //   - or (p + x) mod N otherwise
      )) & v ?                //   if this vertex was already visited:
        s                     //     leave s unchanged
      :                       //   else:
        s +                   //     add to s
        g(                    //     the result of a recursive call:
          (i >= N) / p ?      //       if p and x are connected (i >= N and p is defined):
            [ ...e,           //         append to e[]:
              1 << p | m      //           the edge formed by p and x
            ]                 //           and uniquely identified by 1 << p | 1 << x
          :                   //       else:
            e,                //         leave e[] unchanged
          v | m,              //       mark the vertex x as visited
          x                   //       previous vertex = x
        ),                    //     end of recursive call
      g[e.sort()] ^           //   sort the edges and yield 1 if this list of edges has not
      (g[e] = 1)              //   already been encountered; either way, save it in g
    )                         // end of reduce()
)``                           // initial call to g with e = ['']

2

Jelly, 61 58 bytes

®R,³;Ø+
Ḥ©Ḷµ1ị+¢%®ḟ€;€€1¦-Ẏ;€)Ẏ$ƬẎṣ€-Ẉ’ẠƊƇU¹Ø.ị>/Ɗ?€€Ṣ€QL‘

Try it online!

This is the shorter version; it is optimised for shorter length versus algorithmic complexity and speed.

Jelly, 85 bytes

%®ḟ
1ị+³;Ø+¤ç,1ị+®_3¤R‘¤Ʋç;€-Ʋ“”2ị>-Ʋ?Ẏ;€
Ḥ©Ḷ;€-Ç€Ẏ$ƬẎṣ€-Ẉ=1ẸƊÐḟU¹1ị>0ị$Ʋ?€€Ṣ€QL‘+=2$

Try it online!

Here’s a longer version that adds extra code to avoid trying redundant paths. The check for n=2 at the end is to cope with the specific case for n=2 which looks like a red/blue cross in the example and isn’t generated by this code. This second version completed n=4 in less than 13 seconds on TIO, but times out for higher numbers.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.