改良されたLanskyアルゴリズムに従って単純な音節アルゴリズムを実装しましたが、200万語を超えるコーパスでこのアルゴリズムを実行する必要がある場合は非常に遅くなります。誰かが私をそんなに遅くする原因の方向に向けることができますか?以下のアルゴリズム:
最後の母音(母音グループ)以降はすべて最後の音節に属します
最初の母音(母音グループ)の前のすべてが最初の音節に属します
母音間の子音の数が偶数(2n)の場合、前半は左母音に属し、後半は右母音に属する半分に分割されます(n / n)。
母音間の子音の数が奇数(2n + 1)の場合、それらをn / n +1の部分に分割します。
母音の間に子音が1つしかない場合、それは左の母音に属します。
#include <stdio.h>
#include <string.h>
#define VOWELS "aeiou"
int get_n_consonant_between(char *word, int length) {
int count = 0;
int i = 0;
while (i++ < length) {
if (strchr(VOWELS, *word)) break;
word++;
count++;
}
return count;
}
void syllabification(char *word, int n_vowel_groups) {
int i = 0, length = strlen(word), consonants;
int syllables = 0, vowel_group = 0, syl_length = 0;
char *syllable = word;
char hola[length];
memset(hola, 0, length);
if (n_vowel_groups < 2) {
printf("CAN'T BE SPLIT INTO SYLLABLES\n\n");
return;
}
while (i < length) {
if (strchr(VOWELS, word[i])) {
syl_length++;
i++;
if (vowel_group) continue;
vowel_group = 1;
}
else {
if (vowel_group) {
consonants = get_n_consonant_between(word + i, length - i);
if (consonants == 1) {
// printf("only one consonant\n");
syl_length++;
strncpy(hola, syllable, syl_length);
i++;
}
else {
int count = consonants / 2;
if ((consonants % 2) == 0) { /* number of consonants is 2n, first half belongs to the left vowel */
syl_length += count;
}
else {
syl_length += count;
}
strncpy(hola, syllable, syl_length);
i += count;
}
syllables++;
if (syllables == n_vowel_groups) {
printf("syllable done %d: %s\n", syllables, syllable);
break;
}
printf("syllable %d: %s\n", syllables, hola);
syllable = word + i;
syl_length = 0;
memset(hola, 0, length);
}
else {
syl_length++;
i++;
}
vowel_group = 0;
}
}
}
int count_vowel_groups(char *word) {
int i, nvowels = 0;
int vowel_group = 0;
for (i = 0; i < strlen(word); i++) {
if (strchr(VOWELS, word[i])) {
if (vowel_group) continue;
vowel_group = 1;
}
else {
if (vowel_group) nvowels++;
vowel_group = 0;
}
}
// printf("%d vowel groups\n", nvowels);
return nvowels;
}
void repl() {
char *line = NULL;
size_t len = 0;
int i = 0;
int count;
FILE *file = fopen("../syllables.txt", "r");
while(i++ < 15) {
getline(&line, &len, file);
printf("\n\n%s\n", line);
count = count_vowel_groups(line);
syllabification(line, count);
}
}
int main(int argc, char *argv[]) {
// printf("Syllabification test:\n");
repl();
}
`
これは、実装が正しいかどうかを確認するために実行する必要のある多くのコードです。主な理由は、アルゴリズムの用語(正確には母音グループとは何かなど)がわからないためです。調べてみると、グーグルがさまざまな言語の音節化に関する多くの研究論文(要約しか見ることができない)を返してきたので、コードがまったく正しいかどうかはわかりません。
しかし、私はあなたのコードをより速くするかもしれないいくつかの提案があります:
すべての人をループ状態strlen(word)
から外しfor
ます。長さを変数に保存し、代わりにその変数を使用してください。だからから
for (i = 0; i < strlen(word); i++)
に
size_t len = strlen(word);
for(i = 0; i < len; i++)
strchr
文字が母音であるかどうかの確認には使用しないでください。これにはルックアップテーブルを使用します。
// as global variable
char vowels[256];
int main(void)
{
vowels['a'] = 1;
vowels['e'] = 1;
vowels['i'] = 1;
vowels['o'] = 1;
vowels['u'] = 1;
...
}
文字が母音であるかどうかを確認する場合:
// 0x20 | c make c a lower case character
if(vowel[0x20 | word[i]])
syl_length++;
i++;
if (vowel_group) continue;
vowel_group = 1;
}
最初の提案はあなたにわずかなパフォーマンスの向上を与えるかもしれません、コンパイラはかなり賢くて、とにかくそれを最適化するかもしれません。2番目の提案は、単なるルックアップであるため、パフォーマンスが向上する可能性があります。最悪の場合strchr
、"aeiou"
アレイ全体を何度も通過する必要があります。1
また、コードのプロファイルを作成することをお勧めします。これとこれを参照してください。
fotenotes
1提案の実行時間を比較する非常に粗雑なプログラムを作成しました。コンパイラが関数を積極的に最適化しないことを期待して、コードをいくつか追加しました。
#include <stdio.h>
#include <string.h>
#include <time.h>
int test1(time_t t)
{
char text[] = "The lazy dog is very lazy";
for(size_t i = 0; i < strlen(text); ++i)
t += text[i];
return t;
}
int test2(time_t t)
{
char text[] = "The lazy dog is very lazy";
size_t len = strlen(text);
for(size_t i = 0; i < len; ++i)
t += text[i];
return t;
}
#define VOWELS "aeiou"
char vowels[256];
int test3(time_t t)
{
char text[] = "The lazy dog is very lazy";
size_t len = strlen(text);
for(size_t i = 0; i < len; ++i)
{
if (strchr(VOWELS, text[i]))
t += text[i];
t += text[i];
}
return t;
}
int test4(time_t t)
{
char text[] = "The lazy dog is very lazy";
size_t len = strlen(text);
for(size_t i = 0; i < len; ++i)
{
if(vowels[0x20 | text[i]])
t += text[i];
t += text[i];
}
return t;
}
int main(void)
{
vowels['a'] = 1;
vowels['e'] = 1;
vowels['i'] = 1;
vowels['o'] = 1;
vowels['u'] = 1;
long times = 50000000;
long tmp = 0;
clock_t t1 = 0, t2 = 0, t3 = 0, t4 = 0;
for(long i = 0; i < times; ++i)
{
clock_t start,end;
time_t t = time(NULL);
start = clock();
tmp += test1(t);
end = clock();
t1 += end - start;
//t1 += ((double) (end - start)) / CLOCKS_PER_SEC;
start = clock();
tmp += test2(t);
end = clock();
t2 += end - start;
start = clock();
tmp += test3(t);
end = clock();
t3 += end - start;
start = clock();
tmp += test4(t);
end = clock();
t4 += end - start;
}
printf("t1: %lf %s\n", ((double) t1) / CLOCKS_PER_SEC, t1 < t2 ? "wins":"loses");
printf("t2: %lf %s\n", ((double) t2) / CLOCKS_PER_SEC, t2 < t1 ? "wins":"loses");
printf("t3: %lf %s\n", ((double) t3) / CLOCKS_PER_SEC, t3 < t4 ? "wins":"loses");
printf("t4: %lf %s\n", ((double) t4) / CLOCKS_PER_SEC, t4 < t3 ? "wins":"loses");
printf("tmp: %ld\n", tmp);
return 0;
}
結果は次のとおりです。
$ gcc b.c -ob -Wall -O0
$ ./b
t1: 10.866770 loses
t2: 7.588057 wins
t3: 10.801546 loses
t4: 8.366050 wins
$ gcc b.c -ob -Wall -O1
$ ./b
t1: 7.409297 loses
t2: 7.082418 wins
t3: 11.415080 loses
t4: 7.847086 wins
$ gcc b.c -ob -Wall -O2
$ ./b
t1: 6.292438 loses
t2: 5.855348 wins
t3: 9.306874 loses
t4: 6.584076 wins
$ gcc b.c -ob -Wall -O3
$ ./b
t1: 6.317390 loses
t2: 5.922087 wins
t3: 9.436450 loses
t4: 6.722685 wins
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加