我创建了一个用于生成随机十六进制颜色代码的小程序。它编译并运行没有错误,但由于我是新手,我正在质疑代码的效率并寻找可能优化的解释。
以下是代码:
#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;
}
同样,程序编译并运行没有错误,但我持怀疑态度。任何提示或更正都将不胜感激。
答案 0 :(得分:4)
char hexadecimal[7];
太小,无法容纳字符串,如"#123456"
,需要8 char
。
重复调用strcat(hexadecimal, hex[r]);
效率低下。可以预期每个strcat()
“花费”现有字符串的长度。要循环n
次,strcat()
一长串n
字符需要O(n*n)
次。更有效地跟踪字符串的结尾并附加在那里。
然而,让我们在没有字符串的情况下这样做。
rand()
会生成int
值[0.. RAND_MAX]
。 RAND_MAX
可能与0x7FFF
一样小,也可能与INT_MAX
一样小。 Example
代码正在寻找生成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倍。
答案 1 :(得分:0)
是的,您的代码效率很低。这是一个了解字符和字符串在C中协同工作方式的好机会。知道这一点,我们可以编写更严格的版本。它会更快地运行,作为奖励,它可能更容易编写和阅读。
(旁注:您的hexadecimal
数组太小了。我们需要'#'
的空间,加上6个十六进制数字,加上终止'\0'
。所以它需要{ {1}}。)
我们将使用字符而不是字符串来构建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] = '#';
但最终这将做同样的事情:生成一个包含六个随机十六进制数字的格式良好的字符串hexadecimal[7] = '\0';
:
hexadecimal
而且,在我走之前,关于效率的脚注。我说“是的,你的代码效率非常低”,我声称我的重写会“更快”地运行。虽然这是真的,但它都是相对的。即使是“效率低下”的版本也会比你测量的速度快得多,两个版本之间的差异几乎难以察觉。
我,无论如何,我会写一个角色的版本,因为我是一个老派的C类型的家伙,我想是这样的。但如果有理由更喜欢printf("%s\n", hexadecimal);
版本 - 如果它更容易或更快地编写,或更容易阅读,或者更容易在第一时间做到正确 - 我们可能更喜欢它,而且只是不要担心它的“低效率”。
为了好玩,我在笔记本电脑上编译并运行了两个版本。 “低效”版本花费了0.002s的用户时间,这是通过Unix'time'命令来衡量的。 “效率更高”的版本耗时... 0.002秒。所以无论差异是什么,它都会小于一毫秒,并且会受到开销问题的影响,比如程序启动所需的时间。
所以我为这两个程序添加了另一个循环,以便它们生成1,000,000个随机十六进制字符串。现在有一个区别:低效版本需要0.404秒,高效版本需要0.263秒。 (想一想:一百万个随机字符串,不到半秒钟。计算机非常非常快。)除去它,低效版本每次迭代需要0.404÷1000000 = .000000404s,或者404 纳秒。另一方面,高效版仅需263纳秒。所以,是的,高效版本效率更高 - 但除非您需要这些随机十六进制值的 lot ,否则您将永远不会注意到差异。
又一个脚注。线
存在严重的潜在问题strcat
最令人讨厌的是,r = rand() % 16;
的实现在低位中不非常随机。因此,如果你只抓住低位,就像你在这里一样,你可以得到一个完全非随机的序列,每次都会经历相同的16个值序列。
解决此问题的最简单方法(假设您可以使用)是切换到更好的rand()
功能:
random()
如果您这样做,则需要使用其随附的r = random() % 16;
功能:
srandom
(旁边的问题:当发现srandom((unsigned) time(&t));
的问题时,为什么没有人只是修复它,而不是让它破坏并发明一个更新但但稍微不那么标准的功能,效果更好,并希望每个人都记得用它代替?老实说我不知道。)
如果您无法使用rand()
,则可以使用高阶位来避开random()
的问题,如下所示:
rand
另请参阅C FAQ list,问题13.16和13.18。