优化C中的()循环

时间:2017-04-04 14:54:17

标签: c for-loop optimization

我的老师说我可以为()循环优化这个,但我看不出怎样,任何帮助都会很棒

void vowels(char strng[])
{
int i, j, word_len, vowels_len, vowel_count;
char vowels[] =  "aeiouAEIOU";
word_len = strlen(strng);     
vowels_len = strlen(vowels);
vowel_count = 0;

for (i = 0; i < word_len; ++i) {         
    for (j = 0; j < vowels_len; ++j) {   
       if (strng[i] == vowels[j]) {
           ++vowel_count;
           break;
       }
    }
}

printf("%s: %d vowels\n", strng, vowel_count);    
}

5 个答案:

答案 0 :(得分:5)

一种方法是完全删除嵌套循环,将其替换为将字符代码映射到表示char是否为元音的标志的数组:

int isVowel[256] = {0};
for (int i = 0 ; vowels[i] != '\0' ; i++) {
    isVowel[(unsigned char)vowels[i]] = 1;
}

现在可以按如下方式优化主循环:

for (int i = 0; i != word_len ; i++) {         
    vowel_count += isVowel[(unsigned char)string[i]];
}

您可以使用switch声明获得相似的效果:

for (int i = 0; i != word_len ; i++) {
    switch(string[i]) {
        case 'a':
        case 'e':
        case 'i':
        case 'o':
        case 'u':
        case 'A':
        case 'E':
        case 'I':
        case 'O':
        case 'U':
            vowel_count ++;
            break;
    }
}

答案 1 :(得分:0)

比Dasblinkenlight更简单的优化是:

git push -v origin master

char vowels[] = "AEIOU"; vowels_len = sizeof(vowels)-1; ... for (char c=strng[i]&~32, j = 0; j < vowels_len; ++j) { if (c == vowels[j]) { 是ascii中的toupper。)

答案 2 :(得分:0)

不确定你的老师是否会考虑这种作弊行为,但这是寻找比使用2个嵌套for循环更优化的元音的一种可能方法。

char *pos;
int vowel_count=0;

pos=strng;
while(pos)
    {
    pos=strpbrk(pos,"aeiouAEIOU");
    if(pos)
        {  
        vowel_count++;
        pos++;
        }
    }

答案 3 :(得分:0)

为了优化代码,您必须首先意识到什么是瓶颈。在算法级别,您有以下基本性能问题:

  • 您必须意识到strlen遍历整个字符串以搜索空终止符,因此您遍历相同的字符串strng两次。这是低效的 - 最好的方法是在检查数据的同时检查空终止。
  • 文字"aeiouAEIOU"是一个常量,因此您可以在编译时知道它的大小。无需使用strlen()在运行时计算此值。您可以使用sizeof代替,这在编译时进行评估。
  • 通过检查大写和小写,你可以加倍工作。相反,暂时将您查找的字符转换为大写字母,然后只查看大写元音以进行匹配。

您还有一个主要错误,即该函数不会返回结果。

“天真”手动优化的第一步是这样的:

#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>

int vowels(const char str[])
{
  const char VOWEL_LOOKUP[] = "AEIOU";
  int vowel_count = 0;

  for(; *str != '\0'; str++)
  {
    char ch = toupper(*str);
    for(size_t i=0; i<sizeof(VOWEL_LOOKUP)-1; i++)
    {
      if(ch == VOWEL_LOOKUP[i])
      {
        vowel_count++;
        break;
      }
    }
  }

  return vowel_count;
}


int main (void)
{
  const char str[] = "Stack Overflow"; // 4 vowels 
  printf("%s: %d vowels\n", str, vowels(str));
}

在高级别上,您将查看算法所需的分支数量,因为这些分支会最大程度地降低性能,阻止CPU的分支预测。

然后,您可以考虑使用查找表替换内部循环。这可能会也可能不会提高性能 - 但至少它会使性能具有确定性。此时你可能会开始牺牲可读性,所以你不应该进一步优化,除非这是一个已知的瓶颈。

查找表版本可能看起来像这个邪恶的,不推荐的代码,它利用了符号表通常按字母顺序列出字母的事实(EBCDIC没有,所以这不会编译在各种疯狂的遗产上系统)。

int vowels(const char str[])
{
  _Static_assert('Z' - 'A' == 25, "Weird symbol table."); // compile-time sanity check 
  const bool VOWEL_LOOKUP['U'-'A'+1] = // compromise between sacrificing RAM or speed
  {
    ['A'-'A'] = true,
    ['E'-'A'] = true,
    ['I'-'A'] = true,
    ['O'-'A'] = true,
    ['U'-'A'] = true,
  };
  int vowel_count = 0;

  for(; *str != '\0'; str++)
  {
    char ch = toupper(*str);
    if(ch >= 'A' && ch <= 'U') // always 2 branches instead of 5
    { // but comes with a bit of calculation overhead:
      ch -= 'A';
      vowel_count += (int)VOWEL_LOOKUP[(size_t)ch]; 
    }
  }

  return vowel_count;
}

这不一定更快......总是以它为基准。

答案 4 :(得分:0)

最快的事情。

void ultravowulator(const char strng[])
{
    char vowels[] = "aeiouAEIOU";
    int vowelscnt = strlen(vowels);    
    int vocabular[256] = {0};   

    for(; *strng != '\0'; strng++) {    
        vocabular[*strng]++;
    }

    int total = 0;
    for (int i = 0; i < vowelscnt; i++){        
        total += vocabular[vowels[i]];
    }
    cout << total << endl;
}

main()
{
    char word[] = "Stack overflow";
    ultravowulator(word);
}