GNU Prolog、98バイト
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
この回答は、Prologが最も単純なI / Oフォーマットでさえ苦労する方法の良い例です。問題を解決するアルゴリズムではなく、問題を説明することにより、真のPrologスタイルで動作します。それは、合法的なバブル配置としてカウントするものを指定し、Prologにそれらすべてのバブル配置を生成してからカウントします。生成には55文字(プログラムの最初の2行)が必要です。カウントとI / Oは、残りの43(3行目、および2つの部分を区切る改行)を取ります。これは、OPが言語がI / Oに苦しむ原因になると期待していた問題ではないと思います!(注:Stack Exchangeの構文強調表示により、これは読みにくくなり、簡単ではないため、オフにしました)。
説明
実際には機能しない同様のプログラムの擬似コードバージョンから始めましょう。
b(Bubbles,Count) if map(b,Bubbles,BubbleCounts)
and sum(BubbleCounts,InteriorCount)
and Count is InteriorCount + 1
and is_sorted(Bubbles).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
どのようにb
機能するかはかなり明確でなければなりません:ソート済みリスト(等しいマルチセットを比較するマルチセットの単純な実装)を介してバブルを表し、1つのバブル[]
のカウントが1で、大きなバブルのカウントがあります内部のバブルの総数に1を足したものに等しい。カウント4の場合、このプログラムは(動作した場合)次のリストを生成します。
[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]
このプログラムはいくつかの理由で答えとしては不適切ですが、最も緊急なのは、Prologには実際にはmap
述語がないことです(そして、それを書くにはバイトがかかりすぎます)。そのため、代わりに次のようなプログラムを作成します。
b([], 0).
b([Head|Tail],Count) if b(Head,HeadCount)
and b(Tail,TailCount)
and Count is HeadCount + TailCount + 1
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
ここでのもう1つの大きな問題は、Prologの評価順序が機能する方法のために、実行時に無限ループに入ることです。ただし、プログラムをわずかに再配置することで無限ループを解決できます。
b([], 0).
b([Head|Tail],Count) if Count #= HeadCount + TailCount + 1
and b(Head,HeadCount)
and b(Tail,TailCount)
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
我々は、彼らが何であるかを知っている前に、私たちは一緒にカウントを追加している- -これはかなり奇妙に見えるかもしれませんが、GNU Prologの者は、#=
非因果算術のその種を処理することが可能である、そしてそれは非常に最初の行だからb
、とHeadCount
とTailCount
必須の両方未満であることCount
(既知)、再帰項が一致できる回数を自然に制限する方法として機能するため、プログラムは常に終了します。
次のステップは、これを少しゴルフすることです。以下のような略語使用して、単一文字の変数名を使用して、空白文字を削除する:-
ためif
と,
のためにand
、使用するsetof
のではなくlistof
(それは短い名前を持っており、この場合には、同じ結果を生成)、および使用するsort0(X,X)
のではなくis_sorted(X)
ので(is_sorted
実際には本当の関数ではありません、私はそれを作りました):
b([],0).
b([H|T],N):-N#=A+B+1,b(H,A),b(T,B),sort0([H|T],[H|T]).
c(X,Y):-setof(A,b(A,X),L),length(L,Y).
これはかなり短いですが、もっと良くすることは可能です。重要な洞察は、[H|T]
リスト構文が進むにつれて本当に冗長になるということです。Lispプログラマーが知っているように、リストは基本的にコンスセルで構成されています。コンスセルは基本的に単なるタプルであり、このプログラムのどの部分もリスト組み込みをほとんど使用していません。Prologにはいくつかの非常に短いタプル構文があります(私のお気に入りはA-B
ですが、2番目のお気に入りはA/B
です。この場合、読みやすいデバッグ出力を生成するため、ここで使用しています)。またnil
、2文字に固執するのではなく、リストの最後に独自の1文字を選択することもできます[]
(私はを選択x
しましたが、基本的には何でも機能します)。の代わりに[H|T]
、を使用してT/H
、から出力を取得できますb
これは次のようになります(タプルのソート順はリストのソート順と少し異なるため、これらは上記と同じ順序ではありません)。
x/x/x/x/x
x/x/x/(x/x)
x/(x/x)/(x/x)
x/x/(x/x/x)
x/(x/x/x/x)
x/x/(x/(x/x))
x/(x/x/(x/x))
x/(x/(x/x/x))
x/(x/(x/(x/x)))
これは、上記のネストされたリストよりも読みにくいですが、可能です。精神的にx
sをスキップ/()
し、バブル(または/
、コンテンツがない()
後、ない場合は単純に縮退したバブル)として解釈し、要素は上記のリストバージョンと1対1(無秩序の場合)に対応します。
もちろん、このリスト表現は非常に短いにもかかわらず、大きな欠点があります。言語に組み込まれていないためsort0
、リストがソートされているかどうかを確認することはできません。sort0
とにかくかなり冗長であるため、手作業で行うことは大きな損失ではありません(実際、[H|T]
リスト表現で手作業で行うことは、まったく同じバイト数になります)。ここで重要な洞察は、リストがその尾がソートされている場合ならば、ソートされている場合書かれた小切手などのプログラムが見ていることであるその尾がソートされ、そのために、冗長なチェックがたくさんあり、それを活用することができます。代わりに、最初の2つの要素が正しいことを確認するだけです(リスト自体とそのすべてのサフィックスがチェックされると、リストが最終的にソートされることを保証します)。
最初の要素には簡単にアクセスできます。それはリストの先頭ですH
。ただし、2番目の要素はアクセスがかなり難しく、存在しない場合があります。幸いなことに、x
(Prologの一般化された比較演算子を介して)検討しているすべてのタプルよりも少ない@>=
ため、シングルトンリストの「2番目の要素」を考慮することができx
、プログラムは正常に動作します。実際に2番目の要素にアクセスする場合、最も簡単な方法は、3番目の引数(out引数)をに追加するb
ことです。これはx
、基本ケースとH
再帰ケースで戻ります。これは、の2回目の再帰呼び出しからの出力としてテールのヘッドを取得できることを意味します。B
もちろん、テールのヘッドはリストの2番目の要素です。だからb
今このようになります:
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
基本ケースは十分単純です(空のリスト、0のカウントを返す、空のリストの「最初の要素」はx
)です。再帰的なケースは、以前と同じ方法で始まります(T/H
でなく表記法[H|T]
を使用H
し、追加の引数として)。頭の再帰呼び出しの余分な引数は無視しJ
ますが、末尾の再帰呼び出しに保存します。次に、リストがソートされていることを確認するために、H
それ以上J
(つまり、リストに少なくとも2つの要素がある場合、最初の要素が2番目の要素以上である)を確認するだけです。
残念ながら、setof
以前の定義c
をのこの新しい定義と一緒に使用しようとすると適合b
しますGROUP BY
。これは、SQLとほぼ同じ方法で未使用のoutパラメーターを処理するためです。必要な処理を実行するように再構成することは可能ですが、その再構成にはコストがかかります。代わりに、findall
より便利なデフォルトの動作を持ち、2文字だけ長いを使用して、次の定義を提供しc
ます。
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
そして、それが完全なプログラムです。バブルパターンを簡潔に生成し、それらをカウントするためにバイトの負荷全体を費やします(findall
ジェネレーターをリストに変換するためにかなり長い時間を必要とlength
します。