私は単語ジェネレーターを構築しました。それは長さを選択し、アルファベットの文字をランダムに選択して単語を構成します。
プログラムは動作しますが、出力の 99% は英語の構造を観察していないためゴミです。
一般的な文字をより頻繁に使用するように RNG にバイアスをかけるための私のオプションは何ですか?
私は stl から rand() を使用しています。
乱数ジェネレーターにバイアスをかけるだけでは適切な英語の単語を作成できないため、出力はまだごみになります。ただし、rngにバイアスをかける1つのアプローチは次のとおりです。
1つの方法は、文字の頻度を使用することです。文字ごとに範囲を定義します:a = [0、2](文字「a」が使用される可能性が2%の場合)、b = [2、5](3%の可能性)など。 0〜100の乱数を使用して、文字を選択します。
もう1つの方法は、特定の遷移を定義できる非決定性有限オートマトンを使用することです(聖書を解析して確率を構築することができます)。したがって、次のような遷移がたくさんあります。たとえば、「a」から「b」への遷移は5%です。次に、オートマトンをウォークスルーして、いくつかの単語を生成します。
適切な用語がマルコフ連鎖であることがわかりました。これはおそらくNFAよりも優れています。
テキストの一部のn-gram分析を実行し、それをバイアスのベースとして使用できます。これは、文字または音節のいずれかで行うことができます。音節による分析の実行は、おそらくもっと複雑です。
文字でやるのは簡単です。ソーステキストの各文字を繰り返し処理し、最後に出会ったn-1文字を追跡します。次に、次の文字ごとに、最後のn-1文字とこの新しい文字(n-gram)を頻度のテーブルに追加します。
この頻度の表はどのように見えますか?n-gramをそれらの頻度にマッピングするマップを使用できます。しかし、このアプローチは、以下で提案するアルゴリズムにはあまり適していません。そのためには、各(n-1)グラムをnグラムの最後の文字の頻度へのマップにマップすることをお勧めします。のようなもの:std::map<std::string, std::map<char,int>>
。
分析を行い、統計を収集すると、アルゴリズムは次のようになります。
異なる重みを持つ値のセットからランダムな値を選択するには、累積度数のテーブルを設定することから始めることができます。次に、周波数の合計よりも小さい数の間の乱数を選択し、それがどの間隔で落ちるかを確認します。
例えば:
次のテーブルを作成します:{A:10、B:17、C:26}。1から26までの数字を選択します。10未満の場合はAです。10以上17未満の場合は、Bです。17より大きい場合は、Cです。
発音可能な単語を作成したい場合は、文字を結合しようとしないでください。
サウンドに参加します。選択するサウンドのリストを作成します:「abe」、「ape」、「gre」など
より現実的な出力を得るために、英語の文字頻度を使用することをお勧めします: http://en.wikipedia.org/wiki/Letter_frequency。
しかし、発音可能な単語が必要な場合は、おそらく音節から生成する必要があります。詳細については、オンラインで確認できます。たとえば、次のURLを参照してください。
ソーステキストを読んでマルコフモデルを導出し、ソースに「似た」単語を生成できます。
これは、単語から文を生成する場合にも機能します。まあ、一種の作品。
単語の文字頻度のみを変更したい場合は、(qu
ペアのように) 語彙分析を行わずに、英語の文字頻度のリストを取得します。
次に、 (1000 分の 1 程度) よりも ( e
7 分の 1 の確率)を出力する可能性が高い加重ランダム ジェネレーターを作成します。x
加重乱数発生器 (rand は整数を生成、IIRC) を生成するには:
1. すべての整数になるように文字の頻度を正規化します (ウィキペディアの頻度は基本的に 100000 を掛けます)
2. ある種のルックアップ テーブルを作成します。下の表のように、特定の範囲を割り当てます
letter | weight | start | end
a | 8.17% | 0 | 8167
b | 1.49% | 8168 | 9659
c | 2.78% | 9660 | 12441
d | 4.25% | 12442 | 16694
e | 12.70% | 16695 | 29396
f | 2.23% | 29397 | 31624
g | 2.02% | 31625 | 33639
.....
z | 0.07% | 99926 | 99999
3. 0 から 99999 までの乱数を生成し、それを使用して対応する文字を見つけます。このようにして、正しい文字頻度が得られます。
まず、次のような文字とその重みを含むテーブルが必要です。
struct WeightedLetter
{
char letter;
int weight;
};
static WeightedLetter const letters[] =
{
{ 'a', 82 },
{ 'b', 15 },
{ 'c', 28 },
// ...
};
char getLetter()
{
int totalWeight = 0;
for ( WeightedLetter const* iter = begin( letters );
iter != end( letters );
++ iter ) {
totalWeight += iter->weight;
}
int choice = rand() % totalWeight;
// but you probably want a better generator
WeightedLetter const* result = begin( letters );
while ( choice > result->weight ) {
choice -= result->weight;
++ result;
}
return result->letter;
}
これは私の頭の中で考えただけなので、エラーが含まれている可能性があります。少なくとも、2 番目のループでは検証が必要です。しかし、それはあなたに基本的な考え方を与えるはずです。
もちろん、これでも英語のような単語にはなりません。"uq" のシーケンスは "qu" と同じように可能性が高く、母音のない単語や母音だけの 10 文字の単語を妨げるものは何もありません。英語の音韻論に関するウィキペディアのページには、どの組み合わせがどこで発生する可能性があるかについての良い情報がありますが、それらに関する統計はありません. 一方、Jabberwocky のように可能な単語を作成しようとしている場合、それは問題にならない可能性があります。1 から最大値までのランダムな数の音節を選択し、次にオンセット、核、およびコーダを選択します。(オンセットとコーダが空になる可能性があることを忘れないでください。)