为啥访问数组中的元素需要固定时间?

Posted

技术标签:

【中文标题】为啥访问数组中的元素需要固定时间?【英文标题】:why does accessing an element in an array take constant time?为什么访问数组中的元素需要固定时间? 【发布时间】:2011-11-10 00:09:39 【问题描述】:

假设我有一个数组:

int a[]=4,5,7,10,2,3,6

当我访问一个元素时,例如a[3],幕后实际发生了什么? 为什么很多算法书(比如 Cormen 的书……)都说它需要一个常数时间?

(我只是低级编程的菜鸟,所以我想向你们学习更多)

【问题讨论】:

只有 10 个赞。这值得关注 【参考方案1】:

数组实际上是由一个内存位置(指针)知道的。访问a[3] 可以在恒定时间内找到,因为它只是 location_of_a+3*sizeof(int)。

在 C 中,您可以直接看到这一点。请记住,a[3]*(a+3) 相同 - 就它的作用而言更清楚一点(取消对“3 项”指针的引用)。

【讨论】:

因为*(a+3)*(3+a) 相同,我们可以将a[3] 也写为3[a],这在IOCCC 等中总是有用的。 :-) @ShreevatsaR:是的,但是......写3[a]并没有很多实际用途 - 但使用*(a+3)在现实场景中通常很有用...... 当使用的索引也是一个变量时,您的解释甚至会成立。但由于这里它是一个常量 (3),所有计算甚至可以由编译器预先完成,并且在运行时程序可以直接访问内存。所以事实上它不仅是恒定的,而且是一个非常小的。【参考方案2】:

一个由 10 个整数变量组成的数组,索引为 0 到 9,可以在内存地址 2000、2004、2008、... 2036 处存储为 10 个字,因此索引为 i 的元素的地址为 2000 + 4 × i。 这个过程需要一个乘法和一个加法。因为这两个操作需要恒定的时间。所以我们可以说访问可以在恒定的时间内执行

【讨论】:

这正是我想要的。它是恒定的,但是这背后的数学是什么----你解释得很好。【参考方案3】:

为了完整起见,“在线性时间内访问什么结构?”以线性时间访问Linked List 结构。要获得 n 元素,您必须遍历 n-1 以前的元素。你知道,就像录音机或 VHS 盒式磁带一样,你必须等待很长时间才能到达磁带/VHS 的末尾 :-)

数组更类似于硬盘:每个点都可以在“恒定”时间内访问:-)

这就是计算机的 RAM 被称为 RAM:随机存取存储器的原因。如果您知道其地址,则可以转到任何位置,而无需遍历该位置之前的所有内存。

有些人告诉我,HD 访问并不是真正的恒定时间(访问我的意思是“定位磁头并读取 HD 的一个扇区的时间”)。我不得不说我不确定。我用谷歌搜索了周围,我还没有找到任何人在谈论它。我知道时间不是线性的,因为它仍然是随机访问的。最后,如果您认为 HD 访问对您来说不够恒定(但是,什么是恒定的?RAM 的访问?考虑 Cache、Prefetching、Data Locality 和 Compiler 优化?),请随意考虑这句话因为数组更类似于 U 盘:每个点都可以在“恒定”时间内访问 :-)

【讨论】:

对于随机存取存储器来说,硬盘是一个不好的例子,因为它们并非如此。也许使用 U 盘或固态磁盘作为示例? 是和否... 从技术上讲,HD 的“外部”部分比内部部分要快,但我们将忽略这一点。访问 HD 上的文件是在恒定时间内完成的(或者至少不是线性时间)。如果您在 HD 上拥有一个文件或在 HD 上拥有一百万个文件,那么访问其中一个文件将花费相同的时间(不完全是,但这更像是一个理论模型,而不是我们生活的物理现实 :-))。 -1可以解释一下吗?我稍微重新考虑了一下。高清访问时间是常数时间。给定一个 HD,你有一个中等的寻道时间。所以寻找文件的开头会花费一些时间。 我没有投反对票,但我怀疑这是由于声称高清访问是恒定时间的。如果HD的读头在盘上的3点钟位置,那么读取另一个扇区所用的时间也受该扇区位置的影响。如果它在 9 点钟位置,则它比在读数头下方的扇区相邻时花费的时间要长得多。毕竟,这就是为什么发明了许多花哨的优化策略来加速 HD 上的伪随机访问的原因。 “恒定时间”仅表示以常数为界,即O(1)。从技术上讲,任何有限媒体都具有O(1) 访问时间,但对于磁带来说,常数非常糟糕,而且时间的缩放使得将其视为磁带可能是无限的更为实际。对于硬盘驱动器(假设没有损坏和无限重试循环),访问时间非常短,出于实际目的应考虑O(1)【参考方案4】:

因为数组是按顺序存储在内存中的。所以实际上,当你访问 array[3] 时,你是在告诉计算机,“获取数组开头的内存地址,然后将 3 加到它上面,然后访问那个点。”由于添加需要固定时间,因此数组访问也是如此!

【讨论】:

其实你加3乘以数组元素的大小。 嗯,是的,但这是需要一章才能准确解释的主题之一。我故意省略细节以避免初学者信息过多。【参考方案5】:

“恒定时间”实际上意味着“不依赖于'问题大小'的时间”。对于“问题”“从容器中取出东西”,“问题大小”是容器的大小。

无论容器大小如何,访问一个数组元素所花费的时间基本相同(这是一种简化),因为使用一组固定的步骤来检索该元素:它在内存中的位置(这也是一种简化)计算,然后检索内存中该位置的值。

例如,链表不能这样做,因为每个链接都指示下一个链接的位置。要找到一个元素,您必须遍历所有以前的元素;平均而言,您将通过一半的容器工作,因此容器的大小显然很重要。

【讨论】:

【参考方案6】:

数组是相似类型数据的集合。所以所有元素都将占用相同数量的内存。因此,如果您知道数组的基地址和它所保存的数据类型,您可以使用以下公式轻松地在恒定时间内获取元素 Array[i]:

int takes 4 bytes in a 32 bit system.
int array[10];
base address=4000;
location of 7th element:4000+7*4.
array[7]=array[4000+7*4];

因此,如果您知道它所保存的数据的基地址和类型,您可以在恒定时间内获得第 i 个元素。 请访问this链接了解更多数组数据结构。

【讨论】:

【参考方案7】:

数组是顺序的,这意味着数组中下一个元素的地址在当前元素的旁边。因此,如果要获取数组的第 5 个元素,则通过将数组的基地址与 5 相加来计算第 5 个元素的地址。这个直接地址现在直接用于获取该地址处的元素。

您现在可以在这里进一步问同样的问题——“计算机如何直接知道/访问计算的地址?”这就是计算机内存(RAM)的本质和原理。如果您对 RAM 如何在恒定时间内访问任何地址进一步感兴趣,您可以在任何计算机组织文本中找到它,或者您可以直接 google。

【讨论】:

【参考方案8】:

如果我们知道需要访问的内存位置,那么这种访问是在恒定时间内进行的。在数组的情况下,内存位置是通过使用基指针、元素索引和元素大小来计算的。这涉及需要恒定时间来执行的乘法和加法运算。因此,数组内的元素访问需要恒定的时间。

【讨论】:

以上是关于为啥访问数组中的元素需要固定时间?的主要内容,如果未能解决你的问题,请参考以下文章

随机存取存储器如何工作?为啥是恒定时间随机访问?

为啥固定元素会减慢 Firefox 中的滚动速度?

为啥要双倍堆叠容量而不是仅仅增加固定数量?

为啥要用Hash

为啥我应该“将 myVariable 转换为最终的一个元素数组”?

为啥我应该“将 myVariable 转换为最终的一个元素数组”?