韻律の列挙


26

「押韻構成は、」文字の文字列であるazから出発して文字の最初の発生が(隙間なく)昇順になるように、、 a。例(最初の出現がマークされている):

abccdbebdcfa
^^^ ^ ^   ^

長さの韻体系の数はベル番号Nによって与えられます B(N)。(OEIS A000110

チャレンジ

あなたの仕事は、これらの韻律体系の列挙、すなわち整数から韻律体系への全単射マッピングを実装することです。正の整数N <= 26と負でない整数が与えられます0 <= i < B(N)。または、範囲を使用できます1 <= i <= B(N)Nすべてiが異なる文字列を生成するように、lengthの韻体系を出力する必要があります。

プログラムまたは関数を作成し、STDIN(または最も近い代替)、コマンドライン引数または関数引数を介して入力を取得し、STDOUT(または最も近い代替)、関数の戻り値または関数(out)パラメーターを介して結果を出力できます。

小文字または大文字を(一貫して)使用できます。

あなたのコードは、妥当な時間内で有効な任意の入力を処理できなければなりません(例えば、数時間を超えないためN = 26、最悪の場合、i)。これによりN、遅い言語でも指数関数的にスケーリングするソリューション(小さなベースの場合)を許可しますが、線形にスケーリングするソリューションi(つまりB(N))を禁止する必要があります。特に、スキームをN破棄するまで、長さのあるすべての有効なライムスキームを繰り返し処理することはできませんi

標準の規則が適用されます。

iスキームへの正確な割り当て(つまり、特定のスキームの順序N)はユーザー次第です。ただし、辞書式順序を選択した場合、ソリューションは次の表に対応する必要があります(-無効な入力を示します)。

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

これは、指定された長さのすべての有効な韻律スキームを生成する短いCJamスクリプトです(ただし、10を超えて試さないでください。

関連する課題


5
Nかなり些細なことではないことがわかり、それを見つけるにはあまりにも愚かだったのであれば、(よくゴルフされた)多項式時間の解決策(に)に賞金をかけるかもしれません。
マーティンエンダー

多項式時間解の恩恵はありますが、制限時間を満たす指数時間解を見たいと思っています。(私自身のMathematicaリファレンス実装は現在も挑戦に勝っています。)
Martin Ender

B(26)は、64ビット整数に収まらない最小のベル番号です。ミーニー。:-(
アンダースカセオルグ16

回答:


3

CJam、68 66バイト

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

オンラインでお試しください。

これが私の最初のCJamプログラムです。Perlソリューションの移植として始まり、当初は130バイトを超えていました。さらにゴルフの提案を歓迎します。

私のPerlプログラムと同様に、2つの部分に分かれています。

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)

パート1で作成された配列をデバッグするには]_`o~、パート1と2の間に追加5します。nがの場合、配列は次のようになります[[1 1 1 1 1 1] [1 2 3 4 5] [2 5 10 17] [5 15 37] [15 52]]。各配列の0インデックスは使用されません。オフセットを計算する必要がないため、簡単になります。配列は次のように計算されます。

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

次の配列を計算する間、古い配列のコピーを保持します。配列は、パート2によって逆の順序で読み取られ、破棄されます。


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)

アルファベット順と0ベースのインデックスを使用します。

l文字の接尾辞の長さをa示し、前の部分で使用された個別の文字の数を示しましょう。次に、p(l,a)残りの文字を選択する方法の数を計算する関数は40バイトになります。

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

ただし、これはチャレンジには遅すぎるため、代わりに必要な値が事前に計算され、u配列に保存されます。計算の各段階で、次の文字がaすでに使用されているものである場合n = k * p(l-1、a)+ n 'ここで、kはアルファベットの0から始まる文字で、n'n次の関数呼び出しの値。残りの文字に関する情報が含まれます。新しい文字が使用される場合、n = a * p(l-1、a)+ n '


1
最悪の場合の入力にはどれくらい時間がかかりますか?
マイケルクライン

1
@MichaelKlein無視できる時間。
-feersum

これはまさに私がやろうとしていたことです(JSでやったことを除く)。良くやった!+1
ETHproductions

11

Haskell(GHC 7.10)、150バイト

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

オペレーターは、長さn # iith(ゼロから始まる)韻体系を計算しますn。Haskellの怠inな無限リストを自動メモ化に利用して、O(n²)(大きな整数)操作で実行されます。サンプルの実行:

*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"]

(最大Nが26ではなく25の場合、.fromEnumB(25)は64ビットに収まるため、削除できますInt。)


1
素晴らしく見える。簡単に理解できるように、ゴルフの少ないバージョンを追加してもよろしいですか?
マイケルクライン

4

Perl 257 + 1(-pフラグ)= 258

Perl 182 + 10(-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}

多くのバイトを節約してくれたdev-nulに感謝します!これで、CJamバージョンの実行から学んだことに基づいて書き直しました。

インデックスを0の昇順で韻を計算します。

2つのパート:パート1は128 90バイトで、パート2のマトリックスを計算します。パート2は129 92バイトで、各文字を計算するための簡単な計算を行います。マトリックスを削除して2つの単純な数値に置き換えることができれば、各数値に対してマトリックスを通る単一のパスを計算し、多くのバイトを節約できます。どうやら、そのアイデアは機能しません!

残念ながら、i9007199254740992を超える値に対して正しい韻を出力しませんが、低い値に対しては美しく機能します! 11バイトのコストでBignumライブラリを追加しました。コマンドラインからを使用して実行されperl -pMbignum bell-rhyme.plます。-pMbignum = 10バイト。また、任意の入力値に対して非常に高速です。


2

OracleのSQL 11.2、412の 284 283バイト

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;

残念ながら、長さは8までしか実行されません。それより大きい値は、次の結果になります。ORA-01489:文字列連結の結果が長すぎます

ゴルフをしていない

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;

aビューは、列aに:i文字を、その値をbに生成します。

再帰ビューvは、パラメーターvとして構築される文字列、cで使用される最後の文字の値、およびnで使用される最大文字の値を取ります。nパラメーターは、重複する文字を含まない文字列の長さに等しく、これが正規表現の目的です。

文字は、その値が<=すでに使用されている最大の文字の値であるか、次に使用される文字である場合に有効です。

どういうわけか、クエリを実行するにはLENGTH(s)<:nの部分が必要であるため、クエリの動作に何か不足しているはずです。

メインのSELECTは、無効な入力と、ターゲットの長さに達する前に作成された短い文字列を除外します。

412バイトバージョン

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;

26で412バイトのクエリを試行しないでください。少なくとも、MacBookのdockerコンテナで実行されているxeバージョンでは、データベースが制限モードになります。職場でエクサデータを試すことはできましたが、残念ながら生計を立てる必要があります。


0

Mathematica、136バイト

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

完全を期すために、ここに私のゴルフのリファレンス実装を示します。既存の答えとは異なり、これは多項式時間では実行されません(N2を底とする指数関数的です)が、時間の制約を満たします(最悪の場合でも30分未満で実行されます)。

アイデアはこれです:

  • 各韻体系について、これまでに最大文字数が増加する位置を特定できます。

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

    これらのマーキングを2進数として扱うことができるため、このようなすべての構造を簡単に反復処理できます。2 n-1から2 n(またはその逆)に反復する必要があります。これが指数関数的な時間の複雑さの原因です。

  • そのような構造ごとに、そのような文字列の数を簡単に判断できます。マーキング間のギャップのみを自由に選択でき、ギャップの前の最大値は、各位置で有効な異なる文字数を示します。これはシンプルな製品です。この数値がより小さい場合i、から減算しiます。それ以外の場合は、要求された韻体系の構造が見つかりました。
  • 与えられた構造内のスキームを列挙するために、単純にi混合基数(またはその残り)を表します。ここで、数字の重みは残りの位置で許可される文字の数によって決まります。

これは、メモや事前計算を必要としないため、提出された他の言語のいくつかでより短いソリューションを可能にするのだろうかと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.