我一直在读一本分配给课程的书,它提到数组访问需要O(1)时间。我意识到这是非常快的(可能尽可能快),但是如果你有一个循环必须引用这几次,分配一个临时变量以使值在数组中查找是否有任何好处?或者使用临时变量仍然可以使用O(1)?
我假设这个问题与语言无关。我也意识到,即使答案是肯定的,优势很小,我只是很好奇。
答案 0 :(得分:6)
注意O(1)并不意味着“瞬间”。它只是意味着“至多是一些不变的”。这意味着1和10 1000 都是O(1),即使其中第二个大于宇宙中的原子数。
如果多次重复访问同一个数组元素,则每次访问需要O(1)次。将该数组元素存储在局部变量中也会产生O(1)查找时间,但常量可能不同。 可能更好地选择一个选项而不是另一个选项,但你真的必须对该程序进行分析以确定。
在实践中,这种微优化不太可能对程序时间产生可测量的影响,除非您运行的代码占用程序运行时间的很大一部分。我很震惊地找到一个例子,这个变化会对任何真实的代码产生明显的影响。
现代架构可能会使这种变化更快一些,但并不是那么显着。如果你多次访问同一个数组元素,处理器可能会将数组的那部分保留在缓存中,使查找速度非常快。此外,良好的优化编译器可能已经将非本地复制代码转换为本地复制代码。
希望这有帮助!
答案 1 :(得分:4)
如果我理解,你问是否
for (int i=0; i<len; i++) {
int temp = ar[i];
foo += temp;
bar -= temp;
}
比任何更好:
for (int i=0; i<len; i++) {
foo += ar[i];
bar -= ar[i];
}
我不担心:
如果循环体中的代码要访问相同的数组条目,多次说ar[i]
,那么任何中途不错的编译器(在非零优化级别)都会将该值保存在寄存器中以便快速再利用。换句话说,编译器可能会在给定上述任一代码示例的情况下生成完全相同的程序集。
请注意,其中任何一个仍然是O(1)(一次访问一件事)。不要将算法的大O符号与指令级优化混淆。
修改强>
我刚编译了一个包含上述两个样本的两个函数的示例程序,并且在-O2
gcc 4.7.2生成了完全相同的机器代码;字节对字节。
答案 2 :(得分:1)
你可以比O(1)时间表现更好的唯一方法就是不必首先做任何事情。那将是O(0)时间。
或用较少的单词:否。
答案 3 :(得分:0)
现代CPU硬件(例如缓存行)中已经内置了一些内容,它们可以执行您所描述的内容,但更好的方式是临时变量无法做到的。更好的是,不需要修改源代码。
答案 4 :(得分:0)
没有。阵列访问不是由闪闪发光和爱情制成的一些神奇的零足迹。可以看到从C中的数组索引确定地址的算法here。您在阵列上拥有的维度越多,访问速度就越慢,因为需要额外的操作(主要是mul
和条件,以成本计算)才能到达最终的1D内存地址。即使您的数组只有一个维度,您仍然需要计算基址的偏移量,这是一个add
运算,因此为O(1)。