一緒に、文脈自由言語のあらゆる生成を呼び出す最小長の文字列のセットを生成する


7

問題(tl; dr)

文脈自由文法を考えると、、取る文字列のセットを見つける、それが少なくとも一度持つすべての生産を通じてを。GG

どのように、そしてどのくらい速くそれを行うことができますか?

バックグラウンド

私はパーサーがYacc + Antlrのようなツールで実装されているコンパイラーに取り組んでいます。ほとんどのパーサーコードを作成しましたが、文法のすべてのプロダクションを少なくとも1回呼び出すオブジェクト言語のコードを生成して、パーサーにフィードして問題がないことを確認したいと思います。 。

良いテストのために、私が本当に欲しいのは、特定のプロダクションが「テスト中」である1つの短いテストファイルです。したがって、各プロダクションルールについて、パーサーを取得する最小限の文字列を生成します。テスト中のプロダクションから一連の端末への状態の開始

可能な解決策

グラフ理論を使用したエレガントなソリューションがあると思いますが、それが何かはよくわかりません。ダイクストラのアルゴリズムを使用して適切な構造を通る最短パスを検索したいのですが、文字列はパスではなくツリー構造の文脈自由文法によって解析されると思います。そのため、どうすればよいかわかりません。 。

それをネットワークフローの問題として提起するための巧妙な方法があるのではないかと思います。次のようなもの:すべてのシンボル(終端と非終端)の頂点とすべての生成の頂点を持つグラフを取り上げます。非ターミナルにプロダクションがある場合、非ターミナルからプロダクションに有向エッジを追加します。プロダクションがシンボルを生成する場合、プロダクションからシンボルに有向エッジを追加します。容量ソースを追加し、開始シンボルに対応する頂点にアタッチします。無限の容量のシンクを追加して、各端子に取り付けます。c

非終端が容量アーク内にある場合、非終端から容量各生成物に弧を追加します。プロダクションに容量インアークがあり、アウトアークからターミナルがある場合、プロダクションから各非ターミナルにキャパシティアークを追加します。kkknkn

次に、ネットワーク上でいくつかの最大フローアルゴリズムを実行し、プロダクションを開始シンボルから端末まで「トリクルダウン」させます。最終的には、フローがソースから出てくるはずであり、ヒットしたすべての端末をゼロ以外のフローで結果文字列として返すことができます。次に、実行ごとに時間の複雑さのようなものになります。ここで、は文法内の終端と非終端の数の合計です-悪くありません。cO(n3)n

ただし、このグラフがどのように見えるかはまだわかりません。無限である必要があると思います。無限のフローネットワークの最大フローを見つけることができるかどうかはわかりません。それを過ぎると、プロダクションを「削除」する方法がわからないので、テストを実行するたびに新しいプロダクションを取得することが保証されます。

私はグーグルで検索して何も見つかりませんでした。この問題に対する素晴らしい解決策はありますか?


1
入力文法があいまいですか?もしそうなら、文字列はすべての派生のすべての作品に対してカウントされますか?文法が削減されている、つまり「行き止まり」のルールが含まれていないと想定できますか(すべてのルールは開始記号から到達可能で、単語に派生できる右側にあります)。
ラファエル

@Raphael私が取り組んでいる特定の文法は、いくつかのマイナーな場所ではあいまいですが、必要に応じて簡単にするために無視できます。パーサにすべてのコードブランチを強制的にヒットさせることを目的としているので、あいまいな文字列は可能なすべての派生に数えない方がいいですが、私が言うように、それは不可能である可能性があります。文法が減ります。
Patrick Collins、

後で試してください。右側(RHS)が終端文字列(空の単語、つまり端子の空の文字列を含む)であるルールのセットを作成します。次に、残りのすべてのルールを順番に調べて、セット内のルールを使用して終端文字列に導出できるRHSがあるかどうかを確認し、これを自分で完了してみます。ただし、2つの注意点があります。コンテキストフリーの文法に関する知識が非常に限られているため、「Yacc + Antlrに類似した」パーサーをプログラミングしたことに非常に驚いています。2番目のポイントは、テストが十分であるとは思えないことです。ルールは解析プロセスで相互作用する場合があります。
バブー2014

確かに、コードカバレッジは非常に大まかなテスト戦略であることが知られています。さらに、曖昧な構文を持つプログラミング言語がありますか?まあ。
ラファエル

@Raphaelプログラミング言語の曖昧な文法は私を悩ませません。私は長年彼らを擁護してきました。USA Defenceはこれを約30年間使用しています。しかし、このテクノロジーは、あいまいさがどの程度あいまいであるかによって異なります。テクノロジをYacc + Antlrのようにする場合、パーサーは言語が明確であるかのように動作します。他のテクノロジーはあいまいさを保つかもしれません。しかし、それでも問題には存在しないように見えるレベルの理解が必要であるという事実に、私はまだ悩まされています。
バブー2014

回答:


3

手短に

文献を十分に知らなかったので、次のセクションで説明する解決策と、最も難しい部分の証明を考え出しました。必要なものがわかったら、適切なアイデアを探すために文献を検索することができました。これは、文献に基づいたアルゴリズムの簡単な説明です。これは、基本的に私が開発したものと同じです。

最初にすることは、サイズが最小の終端文字列を見つけることです σ(U) すべての非ターミナル U文法の。これは、コングラフまたはCFグラマーとしても知られるKnuthの拡張機能、およびダイクストラの最短経路アルゴリズムのグラフを使用して行うことができます。Knuthの論文の例Bは、ほとんど必要なことを実行します。

実際、Knuthはこれらの終端文字列の長さのみを計算しますが、このような終端文字列を実際に1つ計算するようにアルゴリズムを変更するのは非常に簡単です。 σ(U) 非端末ごとに U(以下の自分のバージョンで行うように)。また、σ(a)=a すべてのターミナル a、そして私たちは拡張します σ いつものように文字列準同型に。

次に、非終端がノードであり、アークがある有向グラフを考えます。 (U,V) ルールがある場合 UαVβ。複数のそのようなルールが同じ弧を生成できる場合(U,V)、私たちは長さが |σ(αβ)|最小限です。弧はそのルールでラベル付けされ、その最小の長さは |σ(αβ)| 弧の重みになります。

最後に、ダイクストラの最短経路アルゴリズムを使用して、最初の非終端からの最短経路を計算します S文法の各非終端に。非ターミナルの最短経路を考えるU、弧のルールラベルは、派生を取得するために使用できます。 SαUβ。次に、フォームのすべてのルールにUγ 文法では、サイズ最小の端末文字列を関連付けます σ(αγβ) これは、そのルールを使用して導出できます。

複雑さを軽減するために、ダイクストラのアルゴリズムとKnuthの拡張機能の両方が、ヒープ、別名優先キューで実装されています。これにより、ダイクストラのアルゴリズムは次のように複雑になります。O(nlogn+t)、およびKnuthのアルゴリズムの複雑さ O(mlogn+t)、 どこに〜がある m 文法規則と n 非端末、および tすべてのルールの全長です。全体は、Knuthのアルゴリズムの複雑さに支配されています。mn

以下は、上記の短い答えを出す前の自分の作品です。

無駄なシンボル除去アルゴリズムからソリューションを導き出します。

このアルゴリズムにはいくつかの側面があります。より直感的にするために、私はそれを次第に多くの機能を導入する3つの連続したバージョンで提示することにしました。最初のバージョンは質問に答えませんが、解決策を提案する無用なシンボル除去のための標準アルゴリズムです。2番目のバージョンは、最小性の制約なしで質問に答えます。3番目のバージョンは、質問への回答を提供し、最小限の制約を満たします。この3番目のソリューションは、ダイクストラの最短経路アルゴリズムの and-orグラフへの適応を使用することで改善されます

最終結果は非常に単純なアルゴリズムであり、すでに行われた計算の再検討を回避します。しかし、それは直感的ではなく、証明が必要です。

この回答は、OPのコメントで正確に説明されている質問にのみ答えようとします。「各プロダクションルールについて、パーサーを開始状態からテスト中のプロダクションを通じて一連の端末に渡す最小限の文字列を生成します。 "したがって、私は各ルールについて、ルールを使用して派生した言語のサイズ最小文字列の1つである文字列がセットにあるような文字列のセットを取得することのみを試みます。

ただし、文字列がルールを「呼び出す」、つまりそのルールを使用して派生したという事実は、曖昧な文法を処理し、あいまいさを任意に解決するパーサーによってルールが考慮されることを必ずしも意味しないことに注意する必要があります。そのような状況を処理するには、おそらくパーサーのより正確な知識が必要であり、より複雑な質問になる可能性があります。

基本的なアルゴリズム

この問題を解決するために、文脈自由文法での無用なシンボル除去のための古典的なアルゴリズムから始めることができます。これは、Hopcroft&Ullman、1979年版のセクション4.4、88-89ページにあります。ただし、ここでのプレゼンテーションは少し異なる場合があります。

このアルゴリズムは、OPから要求されたようなカバーの存在を証明することを目的としており、次の2つの部分で構成されています。

  • H&Uの補題4.1、88ページ:すべての非生産的な非終端記号の削除。これは、派生可能な端末文字列を端末ごとに検索しようとすることで行われます。それを説明する簡単な方法は次のとおりです:セットを作成しますProdすべてのターミナルで初期化するod生産的シンボル。次に、まだ処理されていない各ルールについて、右側(RHS)記号がすべて含まれていますProd、左側(LHS)の非終端記号をセットに追加します Prod、および処理するルールのセットから同じLHS非終端のすべてのルールを削除します。すべてのRHSシンボルを含むルールがなくなるまでプロセスを繰り返しますProd。にない残りの非ターミナルProd このプロセスの最後では、非生産的です。これらは終端文字列に派生できないため、文法から削除できます。

  • H&Uの補題4.2、89ページ:到達できないすべてのシンボルの削除。これは、非終端記号をノードと見なし、弧を持つことにより、有向グラフの従来のノード到達可能性によって行われます。(U,V) ルールがある場合 Uα そのような V で発生します α。セットを作成しますReach 初期シンボルのみで初期化された到達可能なシンボルの数 S。次に、すべての非終端記号に対してUReach または後でそれに追加され、すべてのルールに対して Uαに追加します Reach のすべてのシンボル α。すべての非端末がReach このように処理された、に含まれていないすべてのシンボル(端末または非端末) Reach最初のシンボルから派生した文字列には出現できないため、役に立たない。したがって、それらは文法から削除できます。

これらの2つの基本的なアルゴリズムは、文脈自由言語と通常のセットの共通部分に使用されるような、いくつかの文法構築手法の生の結果を単純化するのに役立ちます。特に、これは一般的なCFパーサーの結果をクリーンアップするのに役立ちます。

それらを使用するルールが言語の文字列によって「呼び出される」(つまり、その派生で使用される)ことができないため、無用な非終端記号の削除が、質問の解決のコンテキストで必要です。

すべてのルールを呼び出す一連の文字列を作成する

(まだ最小限の文字列を探しているわけではありません。)

さて質問に具体的に答えると、到達不能なシンボルであろうと生産的でない非終端シンボルであろうと、LHSのような役に立たない非終端をもつ役に立たないルールと同様に、すべての役に立たないシンボルを確かに取り除く必要があります。ターミナル文字列の解析中にこれらが有効に呼び出される可能性はありません(ただし、削除されない場合、パーサーの処理時間を浪費する可能性があります。どのテクノロジーが時間を浪費するかは、パーサーテクノロジーによって異なります)。

次に、各(有用な)ルールについて、それを呼び出す、つまりこのルールを使用して生成される可能性のある端末ストリングの生成について検討します。これは基本的に上記の2つのアルゴリズムによって行われますが、非端末が到達可能で生産的であることを保証するためにこれらの文字列の存在を証明することに満足しているため、情報は保持されません。

最初のアルゴリズム(補題4.1)を、それぞれの非端末に合わせて変更します。 U セットで Prod 端末文字列 σ(U) それはに由来します: Uσ(U)。すべてのターミナルについて、σアイデンティティマッピングとして。いつU セットに追加されます Prod ルールのため Uγ すべてのRHSシンボルが Prod、次に定義します σ(U)=σ(γ)、拡張 σ 文字列の準同型として、すべて削除します U-rules、つまりすべてのルール U LHSとして。

2つ目のアルゴリズム(補題4.2)を、各非終端記号に合わせて変更します。 U に追加 Reach 最初のシンボルから到達するために使用されるパス S、派生を取得するための連続したルールを提供します SαUβ

次に、各ルールについて Uγ文法では、次のようにこのルールを「呼び出す」終端文字列を生成します。2番目のアルゴリズムの結果から導出 SαUβ。次に、ルールを適用して文字列を取得しますαγβ。ルールを「呼び出す」終端文字列Uγ です σ(αγβ)

すべてのルールを「呼び出す」最小限の文字列のセットを構築する

これらの変更されたアルゴリズムの副産物である可能性のある不要なシンボルを排除する問題は無視します。

最小限の文字列のセットを構築するには、最初に各非端末の最小限の派生文字列を取得する必要があります。これは、最初のアルゴリズム(補題4.1)をさらに変更することによって行われます。まず、処理するルールのセットからすべての再帰ルールを削除します(つまり、RHS文字列にLHSシンボルが含まれています)。これらのルールのいずれも、同じLHSを持つ非再帰ルールよりも短い終端文字列を導出できないことは明らかです。また、LHSが無意味な非終端でない場合(非生産的であるため)、少なくとも1つの非再帰的ルールが必要です。

次に、以前と同様にセットを構築します Prod 各シンボルに関連付けられた生産的シンボルの U ターミナル文字列。 σ(U)。文字列σ(U) ルールの適用により以前と同様に生成されます Uγ、各非終端を置き換える V で発生 γσ(V)。これまでのところ、これは特定の非終端記号を持つ1つのルールのみに適用する必要がありました。U そのLHSとして、最初にすべてのRHS非端末が Prod、他の文字列は無視されるため、他の文字列は無視してください。しかし、今は最小限の派生文字列を探しています。したがって、非端末の場合U、これはすべてのルールで実行する必要があります ULHSとして。ただし、1つの終端文字列のみを保持しますσ(U)、新しいものが小さい場合は常に、現在のものを新しく見つかったもので置き換えます。

さらに、文字列はいつでも σ(U) より小さいルールに置き換えられます。 U変更により、より短い文字列でRHSを導出できるため、すでに処理されたRHSで、処理するルールのセットに戻す必要があります。したがって、これを行うと、より多くの反復が必要になりますが、これらの文字列は空の文字列よりもはるかに短くなることがないため、最終的には終了します。

この最初のアルゴリズムの最後に、文字列 σ(U) から導出できる最小の文字列の1つ U。他にもあるかもしれません。

ここで、すべての非ターミナルについて、取得する2番目のアルゴリズムも変更する必要があります。 U、(の1つ)唯一の非終端記号としてUを含む最短の文字列。これを行うには、ノードとして非ターミナルを使用し、アークを持つ同じ有向グラフを保持します(U,V) ルールがある場合 UαVβ。ただし、アークに重みを付けて、到達可能な非ターミナルに関連付ける必要があるターミナルコンテキストの最小長を計算します。弧に関連付けられた重み(U,V) 上記は長さです |σ(αβ)|、マッピング σアイデンティティとしてターミナルに拡張され、文字列準同型として再び拡張されます。文字列から導出できる最短の終端文字列(の1つ)の長さです。 αβ。ご了承くださいVこの計算では削除されます。ただし、いくつかの発生がある場合VRHSでは、1つだけを削除する必要があります。いくつかの可能性があります(U,V) いくつかのルールがある場合、重みが異なる弧 U LHSおよび VRHSで。そのような場合、そのような明るいアーク(の1つ)だけが保持される。

このグラフでは、ノードの到達可能性だけを探すのではなく、 S、ただし最初のシンボルからすべてのノードに到達する最短の重み付きパスの場合 S。これは、ダイクストラのアルゴリズムで実行できます。

非ターミナルの最短経路を考える U、以前のように一連のルールとして読み取り、そこから派生を取得します SαUβ。次に、フォームのすべてのルールにUγ 文法では、次のようにこのルールを「呼び出す」最小限の終端文字列を生成します σ(αγβ)

備考:同じ最小限の文字列が複数のルールで使用される可能性があります。しかし、文字列の1つがルールを使用しているという事実ρ その派生では、必ずしもそのルールの最小文字列であることを意味するわけではありません ρ、別のルールで検出された可能性があるのに対し、より短いルールは ρ。柔軟性がある場合は常に、いくつかの優先順位ポリシーを使用することにより、同じ最小文字列が複数のルールで見つかる可能性を高めることができます。しかし、それは問題を起こす価値がありますか?

非端末から派生する最小限の端末文字列のためのより高速なアルゴリズム

関数の作成 σ そのような σ(U) から派生する最小限の終端文字列です U上記のかなり素朴な手法を使用して、いくつかの非終端に対して新しい小さな派生文字列が見つかったときに、すでに行われた作業を繰り返し再検討する必要があります。プロセスが明らかに終了する場合でも、これは無駄です。

ここでは、より効率的なアルゴリズム、つまり本質的に、And-orグラフのパスコンセプトを適切に定義して、ダイクストラの最短パスアルゴリズムをand-orグラフに拡張したCF文法グラフへの適応を提案します。 。アルゴリズムのこのバリアントはおそらく文献に存在します(それが正しいと仮定して)が、アクセスできるリソースでそれを見つけることができませんでした。したがって、私はそれを証明とともにより詳細に説明しています。

以前と同様に、最初に、処理するルールのセットからすべての再帰ルール(つまり、RHS文字列にLHSシンボルが含まれるルール)を削除します。これらの再帰ルールのいずれも、同じLHSを持つ非再帰ルールよりも短い終端文字列を導出できないことは明らかです。そして、LHSU 少なくとも1つの非再帰的である必要があります Uシンボルの場合のルール U(非生産的であるため)役に立たない非ターミナルではありません。これは厳密には必要ありませんが、後で検討するルールの数を減らします。

次に、以前と同様にセットを構築します Prod 各シンボルに関連付けられた生産的シンボルの X ターミナル文字列。 σ(X)から導出可能なサイズ最小の端末文字列です X (以前のアルゴリズムでは、それは終了後にのみ真実でした)。 Prod すべての終端記号で、各終端記号で初期化されます a、私たちは定義します σ(a)=a

次に、すべてのルールを検討します Uγ すべてのRHSシンボルが Prod、そして私たちはそのようなものを選択します σ(γ)最小サイズです。次に追加しますUProdσ(U)=σ(γ)、すべて削除 U-ルール。すべての生産的ターミナルが入力されるまで繰り返しますProd。非ターミナルU、一度入力すると Prod、変更するために再度考慮する必要はありません σ(U) 小さい文字列の場合。

証明

以前のアルゴリズムは多かれ少なかれ直感的に明白でした。グラフのand-or特性のため、これは少しトリッキーであり、証明はより必要と思われます。実際に必要なのは、最後の反復に適用されたときにアルゴリズムの正確性を確立する次の補題だけです。

補題:アルゴリズムの各反復の後、σ(X) から導出可能なサイズ最小の端末文字列です X、 すべてのために XProd

基本ステップは明白です。これは、定義により、すべてのターミナルに当てはまるためです。 Prod 初期化されたとき。

次に、いくつかの非端末が追加された後にそれが真であると仮定します Prod、させて Uγ 新しい非ターミナルを追加するために選択されたルール Prod。このルールが選ばれたのは、 γProd そして σ(γ) すべてのルールのすべてのRHSでサイズが最小であり、RHSが Prod。その後U に追加されます Prod、私たちはそれを証明する必要があります σ(γ) から導出可能なサイズ最小の端末文字列です U

これは明らかに、ルールで始まるすべての派生の場合です Uγ、帰納仮説により、マッピングの適用 σ のすべての非端末が σ are substituted with size-minimal terminal strings deriving from them. Hence no other derivation can produce a shorter terminal string.

We thus consider only derivations starting with another U-rule Uβ, such that βwΣ, where Σ is the set of terminal symbols.

If βProd, then a minimal string it can derive on is σ(β). But, since we chose the rule Uγ, it must be that |σ(β)||σ(γ)|. So the rule Uβ does not derive on a smaller terminal substring.

The last case to consider is when βProd, and we then consider a derivation βwΣ. If that derivation involves only non-trminals in Prod, then βProd, which is a case we have already seen. Hence we consider only derivations that have steps using a rule with its LHS not in Prod. Let Vα be such a rule, such that αProd.There must be at least one such rule since they are partially ordered by derivation order, and wProd.

Thus we have UβμVν. We know that μ and ν derive on a string of size at least 0, and since no V-rule with a RHS in Prod was chosen, they derive on terminal strings of length at least equal to |σ(γ)|. Hence, with the rule Uβ, U derives on a terminal string of length at least equal to |σ(γ)|.


looks plausible but suggest it needs to be converted at least to pseudocode. also it seems plausible the problem or near variants has been studied in the literature somewhere...
vzn

@vzn I doubt this problem has been published, because I do not see an application that does require this minimality. The useless symbols elimination algorithm is a classical technique, found probably in all textbooks on CF languages. And, as I said, it has some applications in general CF parsing. I never heard of pushing it further for producing test sentences. I chose to try explain its derivation, rather than give pseudo code. As it is the answer is already very long. Maybe in a second answer, if that is acceptable on the site ... as the text is becoming too long to handle.
babou

@vzn What could be in the literature is the extension of Dijkstra's shortest path algorithm to and-or graphs. Unfortunately, I do not have access to resources for finding out. I could write it up in a simplified form, without the CF grammar stuff. But then, I do not think it is a big deal. But it does gives the right hints to implement with low complexity.
babou

minimization & "coverings" are common considerations in the theoretical literature. further thought, maybe a normal form either Greibach or Chomsky could be key to understanding it better (there is some connection there also with removing unproductive terminals/nonreachable symbols). think this is worthwhile question for tcs.se, may ask it at some pt there & cite this one (afaik migrations tend to lose comments.)
vzn

@vzn I doubt Greibach or Chomsky normal form will bring any light. This is grammar dependent more than language dependent. Changing the grammar changes the problem. Actually, I first thought it was a homework dump. The minimality requirement does give it some interest, as finding a good algorithm was less obvious, especially my last version. I wrote the proof because I could not convinced myself any other way that it actually works. It is somehow less intuitive that the straight Dijkstra's algorithm from which I derived it.
babou

2

A simple solution

If you don't care too much about the number of strings, there is an easy solution. Basically, for each production, we will generate a string that covers that production.

How do we do that? It's a straightforward worklist algorithm. Suppose we want to cover the production A::=BCx. Basically, we do breadth-first search starting from the start non-terminal S to find a derivation that includes the non-terminal A on the right hand side: initially, all non-terminals are unmarked, and the set of reachable non-terminals is {S}; in each iteration, we pick one unmarked reachable non-terminal, say X, and for each production with X on the left-hand side, we add all the non-terminals on the right to the set of reachable terminals, then we mark X. Repeat until this process finds that A is reachable. When it does, by backtracing, you obtain a derivation of the form SA. You can extend this to a derivation of the form SABCx.

Next, we need to complete the derivation, by picking some way to complete it to be all non-terminals. This is also easy. For each non-terminal, we find the shortest string that is in the language generated by that non-terminals. This can be obtained by a worklist iteration. Let (A) denote the length of the shortest string we've found so far in the language generated by A. Initially, we set (A)= for all non-terminals A, unless there is a production A::=xyz where all the symbols on the right-hand side are terminals; in that case, we set (A) accordingly (e.g., (A)=3 if the production tells us that xyz is in L(A)). Now we iteratively look for a way to find a shorter string. If you have a production like A::=xBCy, you know that (A)2+(B)+(C); so any time you find a new shorter string in L(B) or L(C) (i.e., any time you update (B) or (C) to be smaller), you can check whether this gives you a new shorter string in L(A), and if so, reduce the value of (A)... which may in turn cause other cascading changes. Keep applying all cascading changes until no further changes are triggered. At that point, you have reached a fixed point, and by backtracing, for each non-terminal you know the shortest string (of terminals) that can be derived from that non-terminal.

Now, given your derivation of the form SBCx, find a way to complete the derivation: replace each non-terminal in BCx with the shortest string (of terminals) that can be derived from it.

This gives you a string (of terminals) that covers the production A::=BCx. Do this once for each production, until all of them are covered.

The number of strings is equal to the number of productions. The running time is quadratic: the process is linear-time for each production. (You could probably reduce it to be a linear-time algorithm in total, but I suspect this will be good enough in practice.)

Optimizations

If you want to reduce the number of strings, you might notice that each string will typically cover many productions, so a subset of these strings will probably suffice.

There are many ways you could reduce the number of strings. One simple one is to use the standard greedy approximation algorithm for set cover. Start by generating all of the strings as above, one for each production, and count how many productions each string covers. Keep the string that covers the most productions; that one you definitely want, so add it to your set of keepers. Now some of the productions are covered by this keeper, so we don't need any new strings that cover them again. Thus, you should update your set of counts for each strings: for string s, count the number of productions that are covered by s but aren't covered by any keeper. Pick the string that has the largest number, add it to your set of keepers, and update the counts again. Repeat until all productions have been covered. This will likely give you a significantly smaller set of strings that cover all productions in your grammar.

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