[英]Efficiency of C Code
我创建了一个小程序,用于生成随机的十六进制颜色代码。 它可以编译并运行,没有错误,但是由于我是新手,所以我在质疑代码的效率并寻找可能的优化方法的解释。
这是代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(void) {
int i, r;
time_t t;
char hexadecimal[7];
char* hex[16];
hex[0] = "0";
hex[1] = "1";
hex[2] = "2";
hex[3] = "3";
hex[4] = "4";
hex[5] = "5";
hex[6] = "6";
hex[7] = "7";
hex[8] = "8";
hex[9] = "9";
hex[10] = "A";
hex[11] = "B";
hex[12] = "C";
hex[13] = "D";
hex[14] = "E";
hex[15] = "F";
strcpy(hexadecimal, "#");
srand((unsigned) time(&t));
for (i = 0; i < 6; i++) {
r = rand() % 16;
strcat(hexadecimal, hex[r]);
}
printf("%s\n", hexadecimal);
return 0;
}
再次,该程序可以编译并运行而没有错误,但是我对此表示怀疑。 任何提示或更正将不胜感激。
char hexadecimal[7];
太小,无法容纳需要8个char
的字符串 "#123456"
。
重复调用strcat(hexadecimal, hex[r]);
效率低下。 每个strcat()
都可以“花费”现有字符串的长度。 要循环n
次并用strcat()
一个长串的n
字符,需要花费O(n*n)
时间。 跟踪字符串的末尾并将其附加在其中的效率更高。
然而,让我们做到这一点没有一个字符串 。
rand()
将生成一个int
值[0.. RAND_MAX]
。 RAND_MAX
可以小至0x7FFF
或大至INT_MAX
。 例
代码正在寻找生成6 * 4位的随机数据。 因此,调用rand()
1或2次,而不是6次。
// for rand(), uint32_t, printf(), PRIX32
#include <stdlib.h>
#include <stdint.h>
#include <stdlio.h>
#include <inttypes.h>
int main(void) {
// srand() omitted for simplicity, let OP add back in
uint32_t r = rand();
if (RAND_MAX < 0xFFFFFF) {
r += r*RAND_MAX; // like r = r*(RAND_MAX + 1) yet avoids RAND_MAX + 1 overflow
r += rand();
}
// We only want 24-bits
r &= 0xFFFFFF;
// If RAND_MAX is a Mersenne Number M=2^n-1,
// then `r` value is no more biased than `rand()`
printf("#%06" PRIX32 "\n", r);
}
注意:执行I / O的时间通常比此代码的其余部分多100倍的时间。
是的,您的代码效率很低。 这是学习C语言中字符和字符串协同工作方式的好机会。知道了这一点,我们可以编写一个更紧密的版本。 它的运行速度要快得多,此外,它的编写和阅读也可能会更容易一些。
(请注意:您的hexadecimal
数组太小。我们需要为'#'
加上6个十六进制数字的空间,再加上终止符'\\0'
。因此它必须为char hexadecimal[8]
。)
我们将使用字符而不是字符串来构建hexadecimal
字符串。 因此,让我们让它不是一个字符串数组,而不是hex
是一个字符串数组:
char hex[16];
hex[0] = '0';
hex[1] = '1';
/* ... */
但是,等等,字符数组是一个字符串。 所以我们可以这样写:
char hex[16] = "0123456789ABCDEF";
要不就:
char hex[] = "0123456789ABCDEF";
(两者之间有细微的差别,但是对于此程序,无论哪种方式都没有关系。)
现在,我们不再使用strcat
将hex[r]
字符串连接到我们正在构建的字符串上,而是将hex[r]
字符直接放置在我们正在构建的字符串中:
for (i = 0; i < 6; i++) {
r = rand() % 16;
hexadecimal[i+1] = hex[r];
}
您会注意到,我分配给hexadecimal[i+1]
,而不是hexadecimal[i]
。 我们必须将所有内容“向右移”一位,因为字符串已经包含了开头的'#'
。 做到这一点的另一种方法(尽管在C语言中是不常见的)是使用基于1的循环:
for (i = 1; i <= 6; i++) {
r = rand() % 16;
hexadecimal[i] = hex[r];
}
另外,由于我们一次要在字符串中建立一个字符,因此,我们也可以通过返回并更改来同样地以领先'#'
字符
strcpy(hexadecimal, "#");
至
hexadecimal[0] = '#';
我们还有另一件事要做。 由于我们一直在手工建立一个字符串,因此我们也必须手工处理null终止:
hexadecimal[7] = '\0';
但最终,这将完成相同的操作:生成格式正确的字符串hexadecimal
其中包含六个随机十六进制数字:
printf("%s\n", hexadecimal);
而且,在我开始之前,先做一个关于效率的脚注。 我说:“是的,您的代码效率很低”,我声称重写将“快得多”地运行。 虽然这是事实,但这都是相对的。 即使是“低效”版本,其运行速度也要比您可以衡量的快得多,并且两个版本之间的差异几乎是不可察觉的。
我,我还是会写一次字符的版本,因为我是个老派的C家伙,我也这么认为。 但是,如果strcat
某种原因更喜欢strcat
版本-如果它更容易或更快捷地编写,或者更容易阅读,或者更容易就位,那么我们仍然可以更喜欢它,而不必担心它的版本“效率低下”。
只是为了好玩,我在笔记本电脑上编译并运行了这两个版本。 根据Unix的“时间”命令,“低效”版本占用了0.002秒的用户时间。 “更高效”的版本花费了0.002秒。 因此,不管有什么区别,它都小于一毫秒,并且被开销问题所淹没,例如程序完全启动所花费的时间。
因此,我向两个程序都添加了另一个循环,以便它们生成1,000,000个随机十六进制字符串。 现在有一个区别:低效率版本花费0.404s,高效率版本花费0.263s。 (考虑一下:一百万个随机字符串,在不到半秒钟的时间内。计算机非常非常快。)将其除掉,效率低下的版本每次迭代花费0.404÷1000000 = .000000404s,即404 纳秒 。 另一方面,高效版本仅花费263纳秒。 因此,是的,有效版本更有效-但除非您需要大量这些随机十六进制值,否则您将永远不会注意到差异。
还有一个脚注。 生产线存在严重的潜在问题
r = rand() % 16;
最令人讨厌的是,有rand()
实现在低阶位不是很随机。 因此,如果像这里一样仅抓住低位,则最终可能会得到一个完全非随机的序列,每次都经过16个值的相同序列。
解决此问题的最简单方法(假设您可以使用它)是切换到更好的random()
函数:
r = random() % 16;
如果这样做,则需要使用其伴随的srandom
函数:
srandom((unsigned) time(&t));
(侧面问题:当发现rand()
的问题时,为什么有人不只是解决它,而不是让它坏了,而是发明一个新的但功能稍差的标准函数,效果更好,希望所有人都记得使用它呢?老实说,我不知道。)
如果您不能使用random()
则可以使用其高阶位来躲避rand
的问题,如下所示:
r = rand() / (RAND_MAX / 16 + 1);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.