Hitung skema sajak

26

"Skema sajak" adalah serangkaian huruf auntuk z, sehingga kemunculan karakter pertama dalam urutan menaik (tanpa celah), dimulai dari a. Misalnya (dengan kejadian pertama ditandai):

abccdbebdcfa
^^^ ^ ^   ^

Jumlah skema sajak panjang Ndiberikan oleh nomor Bell B(N) . ( OEIS A000110 )

Tantangan

Tugas Anda adalah mengimplementasikan enumerasi skema sajak ini, yaitu pemetaan bijective dari integer ke skema sajak. Anda diberi bilangan bulat positif N <= 26, dan juga bilangan bulat non-negatif 0 <= i < B(N). Atau, Anda dapat menggunakan rentang 1 <= i <= B(N). Anda harus menampilkan skema sajak panjang N, sehingga setiap imenghasilkan string yang berbeda.

Anda dapat menulis sebuah program atau fungsi, mengambil input melalui STDIN (atau alternatif terdekat), argumen baris perintah atau argumen fungsi dan mengeluarkan hasilnya melalui STDOUT (atau alternatif terdekat), nilai pengembalian fungsi atau parameter function (out).

Anda dapat menggunakan huruf besar atau kecil (secara konsisten).

Kode Anda harus dapat menangani input yang valid dalam jumlah waktu yang wajar (mis. Tidak lebih dari beberapa jam untuk N = 26, kasus terburuk i). Ini harus memungkinkan solusi berskala eksponensial dengan N(untuk pangkalan kecil), bahkan dalam bahasa yang lambat tetapi melarang solusi yang berskala linier dengan i(yaitu B(N)). Secara khusus, itu berarti Anda tidak bisa hanya mengulangi semua skema panjang sajak yang valid Nsampai Anda membuang iskema.

Aturan standar berlaku.

Contohnya

Penugasan yang tepat iuntuk skema to (yaitu urutan skema untuk suatu yang diberikan N) terserah Anda. Tetapi katakanlah Anda memilih pemesanan leksikografis, solusi Anda harus sesuai dengan tabel berikut (dengan -menunjukkan input yang tidak valid):

N\i 1    2    3    4    5    6    7    8    9    10   11   12   13   14   15
1   a    -    -    -    -    -    -    -    -    -    -    -    -    -    -
2   aa   ab   -    -    -    -    -    -    -    -    -    -    -    -    -
3   aaa  aab  aba  abb  abc  -    -    -    -    -    -    -    -    -    -
4   aaaa aaab aaba aabb aabc abaa abab abac abba abbb abbc abca abcb abcc abcd

Berikut ini adalah skrip CJam pendek yang menghasilkan semua skema sajak yang valid untuk jangka waktu tertentu (tetapi jangan mencoba lebih dari 10 atau Anda akan menunggu beberapa saat).

Tantangan Terkait

Martin Ender
sumber
5
Saya mungkin memberi hadiah pada solusi waktu (jumlahnya banyak) polinomial (dalam N), asalkan tidak berubah menjadi cukup sepele dan saya terlalu bodoh untuk menemukannya.
Martin Ender
Meskipun karunia itu untuk solusi waktu polinomial, saya masih ingin melihat solusi waktu eksponensial yang memenuhi batas waktu. (Implementasi referensi Mathematica saya sendiri saat ini masih akan memenangkan tantangan.)
Martin Ender
B (26) adalah nomor Bell terkecil yang tidak cocok dengan integer 64-bit. Pelit. :-(
Anders Kaseorg

Jawaban:

3

CJam, 68 66 byte

r~:W)1a*{__(;\);_,,.*.+}W(*r~{X@\=_2$\/:CX<!{X:C):X;}&C*-C'a+o}W*;

Cobalah online.

Ini adalah program CJam pertama saya. Ini mulai hidup sebagai port solusi Perl saya dan awalnya lebih dari 130 byte. Saran golf lebih lanjut dipersilakan.

Seperti halnya program Perl saya, program ini ada dalam dua bagian.

Part 1:
r~:W                                         | Read the first input (n) and store it in W
    )1a*                                     | Create an array of n+1 1s
        {              }W(*                  | Repeat n-1 times:
         __                                  | Duplicate array twice
           (;\);                             | Remove first element of 1st array. Swap
                                             | arrays. Remove last element of 2nd array
                _,,                          | Duplicate array. Count items. Create range
                   .*.+                      | Multiply arrays. Add 1st array to result

Part 2:
r~                                           | Read the second input (i)
   {                                  }W*    | Repeat n times:
    X@                                       | Push y (initially 1). Bring item 2 (last array) to top
     \=                                      | Swap top two items. Pop array[y] (v)
       _2$                                   | Duplicate v. Copy item 2 (i) to top
          \/:CX                              | Swap i & v. i/v. Store in C (c). Push y
               <!{       }&                  | If !(i/v < c):
                  X:C):X;                    | c = y. ++y (store in X)
                           C*-C'a+o          | i -= c * v. Push y. Push "a". Add c. Print
                                         ;   | Discard top item (integer 0)

Untuk debug array yang dibuat oleh Bagian 1 add ]_`o~antara Bagian 1 & 2. Jika n adalah 5, array akan terlihat seperti ini: [[1 1 1 1 1 1] [1 2 3 4 5] [2 5 10 17] [5 15 37] [15 52]]. Indeks 0 dari setiap array tidak digunakan, mereka hanya membuatnya lebih mudah dengan tidak harus menghitung offset. Array dihitung seperti ini:

[2 5 10 17] [2 5 10 17] [2 5 10 17]        | Duplicate twice
[2 5 10 17] [2 5 10 17] [5 10 17]          | Discard first item of array
[2 5 10 17] [5 10 17] [2 5 10 17]          | Swap last two arrays
[2 5 10 17] [5 10 17] [2 5 10]             | Discard last item of array
[2 5 10 17] [5 10 17] [2 5 10] [2 5 10]    | Duplicate array
[2 5 10 17] [5 10 17] [2 5 10] 3           | Count items in array
[2 5 10 17] [5 10 17] [2 5 10] [0 1 2]     | Integer to range 0 - n-1
[2 5 10 17] [5 10 17] [0 5 20]             | Multiply arrays [2*0 5*1 10*2]
[2 5 10 17] [5 15 37]                      | Add arrays [5+0 10+5 17+20]

Itu menyimpan salinan dari array yang lama sambil menghitung yang berikutnya. Array dibaca dan dibuang dalam urutan terbalik oleh Bagian 2.

CJ Dennis
sumber
13

Python 2, 153

u=[1]*999;i=60;exec"u[i]=i%30*u[i-30]+u[i-29];i+=1;"*900
def x(l,n,a=0):m=u[30*l+a];c=n>=a*m;return'.'*l and chr(65+min(n/m,a))+x(l-1,[n%m,n-m*a][c],a+c)

Ini menggunakan urutan abjad dan pengindeksan berbasis 0.

Biarkan lmenunjukkan panjang akhiran huruf dan amenunjukkan jumlah huruf berbeda yang digunakan di bagian sebelumnya. Kemudian fungsi p(l,a)yang menghitung jumlah cara untuk memilih huruf yang tersisa bisa menjadi 40 byte:

p=lambda l,a:l<1or a*p(l-1,a)+p(l-1,a+1)

Namun, ini terlalu lambat untuk tantangan, jadi alih-alih nilai-nilai yang diperlukan dihitung sebelumnya dan disimpan dalam uarray. Pada setiap tahap perhitungan, jika huruf berikutnya adalah salah satu yang asudah digunakan, n = k * p (l - 1, a) + n 'di mana k adalah huruf 0 yang diindeks dari alfabet dan n' adalah nilai nuntuk panggilan fungsi berikutnya, yang berisi informasi tentang surat-surat yang tersisa. Jika huruf baru digunakan, maka n = a * p (l - 1, a) + n ' .

feersum
sumber
1
Berapa lama untuk input kasus terburuk?
Michael Klein
1
@MichaelKlein Jumlah waktu yang dapat diabaikan.
feersum
Ini persis apa yang saya rencanakan untuk dilakukan (kecuali saya akan melakukannya dengan JS). Pekerjaan yang baik! +1
ETHproduk
11

Haskell (GHC 7.10), 150 byte

s=(1,\_->[]):s
k!((y,b):l@((x,a):_))|let h i|i<x=k:a i|(p,q)<-divMod(i-x)y=p:b q=(x+k*y,h):(k+1)!l
n#i=(['a'..]!!).fromEnum<$>snd(iterate(0!)s!!n!!0)i

Operator n # imenghitung iskema panjang sajak th (zero-indexed) n. Ini berjalan dalam operasi O (n²) (bilangan bulat besar), mengambil keuntungan dari daftar malas tak terbatas Haskell untuk memoisasi otomatis. Sampel berjalan:

*Main> 26 # 0
"abcdefghijklmnopqrstuvwxyz"
*Main> 26 # 1
"abcdefghijklmnopqrstuvwxya"
*Main> 26 # 2
"abcdefghijklmnopqrstuvwxyb"
*Main> 26 # 49631246523618756271
"aaaaaaaaaaaaaaaaaaaaaaaabb"
*Main> 26 # 49631246523618756272
"aaaaaaaaaaaaaaaaaaaaaaaaab"
*Main> 26 # 49631246523618756273
"aaaaaaaaaaaaaaaaaaaaaaaaaa"
*Main> [1 # i | i <- [0..0]]
["a"]
*Main> [2 # i | i <- [0..1]]
["ab","aa"]
*Main> [3 # i | i <- [0..4]]
["abc","aba","abb","aab","aaa"]
*Main> [4 # i | i <- [0..14]]
["abcd","abca","abcb","abcc","abac","abaa","abab","abbc","abba","abbb","aabc","aaba","aabb","aaab","aaaa"]

(Jika maksimum N adalah 25 bukannya 26, .fromEnumbisa dihapus, karena B (25) cocok dalam 64-bit Int.)

Anders Kaseorg
sumber
1
Tampak hebat. Maukah Anda menambahkan versi yang kurang golf untuk grokking yang lebih mudah?
Michael Klein
4

Perl 257 + 1 (-p flag) = 258

Perl 182 + 10 (flag -pMbignum) = 192

($n,$i)=split;@m=[@a=(1)x($n+1)];while($a[2]){push@m,[@a=map{$a[$_]*$_+$a[$_+1]}0..$#a-1]}$_='';$y=1;while($w=pop@m){$c=int($i/($v=$$w[$y]));$c=$y++if($c>=$y);$i-=$c*$v;$_.=chr$c+65}

Terima kasih kepada dev-nul karena telah menghemat banyak byte! Saya sekarang telah menulis ulang berdasarkan apa yang saya pelajari dari melakukan versi CJam.

Menghitung sajak dalam urutan abjad naik, 0 diindeks.

Dua bagian: Bagian 1 adalah 128 90 byte dan menghitung matriks untuk Bagian 2. Bagian 2 adalah 129 92 byte dan melakukan beberapa matematika sederhana untuk menghitung setiap huruf. Jika saya bisa menyingkirkan matriks dan menggantinya dengan dua angka sederhana, saya bisa menghitung jalur tunggal melalui matriks untuk setiap angka dan menyimpan banyak byte! Tampaknya, gagasan itu tidak berhasil!

Sayangnya, itu tidak menghasilkan rima yang tepat untuk nilai yang ilebih tinggi dari 9007199254740992, tetapi bekerja dengan indah untuk nilai yang rendah! Saya telah menambahkan perpustakaan Bignum dengan biaya 11 byte. Dijalankan dari baris perintah dengan perl -pMbignum bell-rhyme.pl. -pMbignum = 10 byte. Ini juga sangat cepat untuk nilai input apa pun.

CJ Dennis
sumber
2

Oracle SQL 11.2, 412 284 283 byte

WITH a AS(SELECT CHR(96+LEVEL)d,LEVEL b FROM DUAL CONNECT BY LEVEL<=:i),v(s,c,n)AS(SELECT d,1,1 FROM a WHERE b=1 UNION ALL SELECT s||d,b,LENGTH(REGEXP_REPLACE(s||d,'([a-z])\1+','\1'))FROM v,a WHERE(b<=n OR b=c+1)AND LENGTH(s)<:n)SELECT s FROM v WHERE:n=LENGTH(s)AND:i<=:n ORDER BY 1;

Sayangnya hanya berjalan hingga panjang 8. Nilai hasil yang lebih besar di: ORA-01489: hasil penggabungan string terlalu panjang

Tidak bermain golf

WITH a AS(SELECT CHR(96+LEVEL)d,LEVEL b FROM DUAL CONNECT BY LEVEL<=:i),
v(s,c,n) AS
(
  SELECT d,1,1 FROM a WHERE b=1
  UNION ALL
  SELECT s||d,b,LENGTH(REGEXP_REPLACE(s||d,'([a-z])\1+','\1')) 
  FROM v,a 
  WHERE (b<=n OR b=c+1) AND LENGTH(s)<:n
)
SELECT s FROM v WHERE LENGTH(s)=:n AND :i<=:n ORDER BY 1;

Tampilan menghasilkan: huruf i pada kolom a dan nilainya dalam b.

Tampilan rekursif v mengambil string yang dibangun sebagai parameter v, nilai huruf terakhir yang digunakan dalam c dan nilai huruf terbesar yang digunakan dalam n. Parameter n sama dengan panjang string tanpa huruf duplikat, itulah gunanya regex.

Sebuah huruf valid jika nilainya <= nilai huruf terbesar yang sudah digunakan atau huruf berikutnya yang digunakan.

Entah bagaimana permintaan membutuhkan PANJANG (s) <: n bagian untuk dijalankan, saya harus kehilangan sesuatu dalam cara kerja kueri.

SELECT utama menangani penyaringan input yang tidak valid dan string yang lebih pendek dibangun sebelum panjang yang ditargetkan tercapai.

Versi 412 byte

WITH a AS(SELECT * FROM(SELECT d,b,ROW_NUMBER()OVER(PARTITION BY b ORDER BY d)l FROM(SELECT CHR(64+DECODE(MOD(LEVEL,:i),0,:i,MOD(LEVEL,:i)))d,CEIL(LEVEL/:i)b FROM DUAL CONNECT BY LEVEL<=:i*:n))WHERE l<=b),v(s,c,p)AS(SELECT d,1,l FROM a WHERE b=1 UNION ALL SELECT s||d,c+1,l FROM v,a WHERE c+1=b AND(l<=LENGTH(REGEXP_REPLACE(s,'([A-Z])\1+','\1'))OR l=p+1))SELECT s FROM v WHERE LENGTH(s)=:n AND :i<=:n ORDER BY 1;

Jangan coba kueri 412 byte dengan 26. Ini menempatkan database dalam mode terbatas, setidaknya pada versi xe saya berjalan dalam wadah buruh pelabuhan di macbook. Saya bisa mencoba exadata di tempat kerja, tetapi sayangnya saya masih perlu bekerja untuk mencari nafkah.

Jeto
sumber
0

Mathematica, 136 byte

(For[j=2^#-1;t=#2,c=1;m=0;x=t;r=If[#>0,++m,c*=m;d=x~Mod~m+1;x=⌊x/m⌋;d]&/@j~IntegerDigits~2;;c<=t,t-=c;--j];FromCharacterCode[r+64])&

Untuk kelengkapan, berikut adalah implementasi referensi golf saya. Berbeda dengan jawaban yang ada ini tidak berjalan dalam waktu polinomial (itu eksponensial Ndengan basis 2) tetapi memenuhi batasan waktu (kasus terburuk masih berjalan di bawah setengah jam).

Idenya adalah ini:

  • Untuk setiap skema sajak kita dapat mengidentifikasi posisi di mana karakter maksimum sejauh ini meningkat:

    ABCDEFGHDIJDEKBBIJEIKHDFII
    ^^^^^^^^ ^^  ^
    

    Kita dapat memperlakukan tanda-tanda itu sebagai angka biner yang membuatnya mudah untuk diulangi pada semua struktur tersebut. Kita perlu beralih dari 2 n-1 ke 2 n (atau sebaliknya) dari mana kompleksitas waktu eksponensial berasal.

  • Untuk setiap struktur seperti itu mudah untuk menentukan berapa banyak string seperti itu: hanya kesenjangan antara tanda dapat dipilih secara bebas, dan maksimum di depan celah memberitahu kita berapa banyak karakter berbeda berlaku di setiap posisi. Ini adalah produk sederhana. Jika angka ini lebih kecil dari i, kita kurangi dari i. Kalau tidak, kami telah menemukan struktur skema sajak yang diminta.
  • Untuk menghitung skema dalam struktur yang diberikan, kami hanya mewakili i(atau apa yang tersisa dari itu) sebagai angka basa campuran, di mana bobot digit ditentukan oleh jumlah karakter yang diperbolehkan di posisi yang tersisa.

Saya bertanya-tanya apakah ini akan memungkinkan solusi yang lebih pendek dalam beberapa bahasa lain yang diajukan karena tidak memerlukan memoisasi atau perhitungan.

Martin Ender
sumber