2

単語の配列とテキスト ファイルがあります。私がやりたいことは、単語の配列を使用してテキスト ファイルを検索し、配列内の各単語がテキスト ファイルに出現する回数を数えることです。

For ループを使用することを考えましたが、それぞれの個々の単語数ではなく、単語数の合計が得られました。テキスト ファイルには約 40000 語あるため、テキスト ファイルを配列に入れることはできません。

カウントの後、各カウントを「スケール」と呼ばれる整数値で割りたいと思います。そして、文字列に新しいカウント数を掛けます。

というわけで、現在は下図のようにしています。とにかくこれをより効率的にすることはできますか?

どんな助けでも大歓迎です。

単語の配列 = テスト単語。

ファイル名 = testF.

inWord = ファイル内の各単語。

while(testF >> inWord)
    {if (inWord == testwords[0]){
            count1++;
            }
        if (inWord == testwords[1]){
            count2++;
            }
        if (inWord == testwords[2]){
            count3++;
            }
        if (inWord == testwords[3]){
            count4++;
            }
        if (inWord == testwords[4]){
            count5++;
            }
        if (inWord == testwords[5]){
            count6++;
            }
        if (inWord == testwords[6]){
            count7++;
            }
        if (inWord == testwords[7]){
            count8++;
            }
}
cout << testwords[0] << " " << count1 << " " << s1.append(count1/scale, '*') << endl;
cout << testwords[1] << " " << count2 << " " << s2.append(count2/scale, '*') << endl;
cout << testwords[2] << " " << count3 << " " << s3.append(count3/scale, '*') << endl;
cout << testwords[3] << " " << count4 << " " << s4.append(count4/scale, '*') << endl;
cout << testwords[4] << " " << count5 << " " << s5.append(count5/scale, '*') << endl;
cout << testwords[5] << " " << count6 << " " << s6.append(count6/scale, '*') << endl;
cout << testwords[6] << " " << count7 << " " << s7.append(count7/scale, '*') << endl;
cout << testwords[7] << " " << count8 << " " << s8.append(count8/scale, '*') << endl;
4

4 に答える 4

4

効率について心配する前に、アプローチについて心配する必要があります。論理データ構造を使用していません。8 つの個別のカウントを持つ代わりに、カウントの配列を保持します。またはさらに良いことに、単語のマップを保持します->カウント。

この状況では幸運なことに、よりクリーンなコードははるかに高速な実行に対応します。

特に、 を使用しstd::map<std::string, size_t>ます。

あるいは、C++11 を使用している場合は、パフォーマンスを向上させるために std::unordered_map を使用できます。

からあなたの言葉を読んでいると仮定しますcin

std::map<std::string, size_t> counts;

std::string word;

while (std::cin >> word) {
    ++counts[word];
}

for (std::map<std::string, size_t::const_iterator it = counts.begin(),
     end = counts.end(); it != end; ++it) {
    std::cout << "The word '" << it->first << " appeared " 
              << it->second << " times" << std::endl;
}

std::map のドキュメント。

std::unordered_map のドキュメント。

価値があるのは、 std::unordered_map が (おそらく常に) hash mapとして実装され、 std::map が (おそらく常に) バッキング構造としてバランスの取れたバイナリ ツリーを使用して実装されていることです。

于 2012-11-17T12:21:10.000 に答える
1

を設定しstd::map<std::string, unsigned long long>、ドキュメントを単語ごとにスキャンし、単語ごとにカウンターをインクリメントします。

std::map<std::string, unsigned long long> wordMap;

std::string word; // read words into this string
...
wordMap[word]++; // increase counter each time a word is found. First call will insert 0.

次に、マップ内のエントリをチェックして、単語の配列をループできます。

for (unsigned int i = 0; i < nWords; ++i)
{
  std::cout << "Word " << testWords[i] << " was found " << wordMap[testWords[i]] << " times\n";
}

新しい単語が見つかるたびにmyMap[word]、キーと値のペアが挿入されますword : 0

C++11 を使用している場合は、 を試して、std::unordered_map最もパフォーマンスの高いものを選択できます。

于 2012-11-17T12:21:04.227 に答える
0

比較する値が8つしかないため、stdよりも優れたハッシュアルゴリズムを見つけることができます。最初の2文字、最後の文字、または文字列の長さのみで構成されます。

while (std::cin >> word) {
  int i=my_hash(word);
  if (word==my_sparse_hash_table[i].word) my_sparse_hash_table[i].count++;
}

あなたの方法を使うだけ:

while (std::cin >> word) {
   for (int i=0;i<N;i++) 
     if (word == myTable[i].word) { myTable[i].count++; break; }
}  // earlies break out of the loop

マイクロ最適化には、見つかったエントリを配列myTableの先頭に向かって移動することが含まれます。

于 2012-11-17T12:34:58.750 に答える
0

ここでの他のすべての回答は非常に良い提案です。実行できる小さな最適化の 1 つは、既存のコードでelseを使用することです。

if (inWord == testwords[0])
{
    count1++;
}
if (inWord == testwords[1])
{
    count2++;
}

で置き換えることができます

if (inWord == testwords[0])
{
    count1++;
}
else if (inWord == testwords[1])
{
    count2++;
}

概念は、inWordが要素 0 と一致する場合、他の要素と一致する可能性は低いということです。

いずれにせよ、プロファイラーはあなたの友達です。

于 2012-11-17T13:09:05.910 に答える