为什么比较字符串是 0(n),而比较数字是 0(1)?
Why is comparing strings 0(n), but comparing numbers 0(1)?
我知道要比较两个字符串是否相等,解释器必须遍历两个字符串并比较每个字符。
这将使时间复杂度 0(n)
其中 n
是最短字符串的长度。
但是,比较两个 数字 是否相等是 0(1)
。
这是为什么?解释器不需要遍历每个数字来检查相等性吗?
通常 程序将数字表示为固定大小的数据结构(二进制值,这就是为什么您会看到它们的大小以位来描述的原因)。作为固定大小,比较将花费恒定的时间并且是 O(1),这是这种表示的好处之一。缺点是可以表示的值的范围受到限制。
解除此限制的替代表示,允许任意大范围的数字,因此大小将不再固定,也不再是 O(1) 进行比较。
字符串
字符串比较通常是字符的线性扫描,在字符不匹配的第一个索引处返回 false。
时间复杂度为O(N),实际耗时取决于需要扫描多少个字符才能统计出差异。
没有一个简单的答案,但答案是显而易见的;-)
数字
如果两个整数相等,不比较它们的所有位是不可能知道的。因此,在相等的情况下,所需的时间与位数成正比(如果 N 是比较数之一,则与 log(abs(N)) 成正比)。
如果它们实际上不相等,则有很多情况,都与实现内部相关。长整数以 2 的幂为基数存储为“数字”向量。如果向量的长度不同,则整数不相等,这需要常数时间。
但如果它们的长度相同,则必须比较“数字”,直到找到第一个(如果有的话)不匹配的对。这花费的时间与需要比较的位数成正比。
计算机中的数字通常以固定大小的单位处理。 int
在任何给定语言 and/or compiler/platform 组合中可能是 32 位或 64 位,但它永远不会是可变长度的。
因此,在比较数字时,要比较的位数是固定的。构建一次比较那么多位的硬件电路非常容易(即 "one action")。
另一方面,字符串具有固有的可变长度,因此您只是说 "string" 并没有告诉您必须比较多少位。
但是 是 例外,因为有可变长度的数字,通常称为 BigInteger
或 BigDecimal
之类的东西,其行为与 String
比较,因为比较两个 BigDecimal
值是否相等最终可能是 O(n)(其中 n 是 BigDecimal
的长度,而不是它们的任何一个数值)。
一般来说,我们只在 n 可以上升到非常大的值时才使用 big-O 表示法,因为 big-O 表示法描述了执行时间如何随着输入的增长而增长。例如,在对列表进行排序时,大多数最好的算法都按 O(n log n)
排序——这意味着, 仅 意味着,当列表足够长时,它的时间排序所需的时间与 n log n
成正比。当列表不够长时,其他因素(例如,您的算法可能需要分配额外 space 的任何时间)会变得很重要,甚至可能会占用 运行ning 时间。
对于 JavaScript 个字符串,n
确实可以任意大*,所以我们说比较需要 O(n)
时间。但是对于 JavaScript 个数字(即 IEEE 754 double-precision floating point numbers),n
的最大上限为 64 - 符号位为 1,指数为 11,有效数字为 53**。正因为如此,我们确切地知道进行数字比较可能需要多长时间,并且我们拥有的用于比较确切大小的数字的最佳系统或多或少 运行 相同,无论这 64 个中有多少每个数字实际具有的数字 - 因此,比较 JavaScript 中的这些数字被认为是 O(1)
.
*从技术上讲,有一个上限,因为 RAM 可以 运行。但是,该语言没有指定字符串的最大大小,并且字符串比较的 O(n)
部分在发生之前就占据了执行时间。
**顺便说一句,这确实意味着 JavaScript 中的数字不能无限增加。过了某个点,他们开始丢弃更小的数字(例如,2^53 以上的数字只能是偶数,2^54 以上的数字只能被 4 整除),当数字足够大时,它会四舍五入到无穷远。反之,如果你把一个数一遍又一遍地除以使其无限小,它最终会四舍五入为零。
我知道要比较两个字符串是否相等,解释器必须遍历两个字符串并比较每个字符。
这将使时间复杂度 0(n)
其中 n
是最短字符串的长度。
但是,比较两个 数字 是否相等是 0(1)
。
这是为什么?解释器不需要遍历每个数字来检查相等性吗?
通常 程序将数字表示为固定大小的数据结构(二进制值,这就是为什么您会看到它们的大小以位来描述的原因)。作为固定大小,比较将花费恒定的时间并且是 O(1),这是这种表示的好处之一。缺点是可以表示的值的范围受到限制。
解除此限制的替代表示,允许任意大范围的数字,因此大小将不再固定,也不再是 O(1) 进行比较。
字符串
字符串比较通常是字符的线性扫描,在字符不匹配的第一个索引处返回 false。
时间复杂度为O(N),实际耗时取决于需要扫描多少个字符才能统计出差异。 没有一个简单的答案,但答案是显而易见的;-)
数字
如果两个整数相等,不比较它们的所有位是不可能知道的。因此,在相等的情况下,所需的时间与位数成正比(如果 N 是比较数之一,则与 log(abs(N)) 成正比)。
如果它们实际上不相等,则有很多情况,都与实现内部相关。长整数以 2 的幂为基数存储为“数字”向量。如果向量的长度不同,则整数不相等,这需要常数时间。
但如果它们的长度相同,则必须比较“数字”,直到找到第一个(如果有的话)不匹配的对。这花费的时间与需要比较的位数成正比。
计算机中的数字通常以固定大小的单位处理。 int
在任何给定语言 and/or compiler/platform 组合中可能是 32 位或 64 位,但它永远不会是可变长度的。
因此,在比较数字时,要比较的位数是固定的。构建一次比较那么多位的硬件电路非常容易(即 "one action")。
另一方面,字符串具有固有的可变长度,因此您只是说 "string" 并没有告诉您必须比较多少位。
但是 是 例外,因为有可变长度的数字,通常称为 BigInteger
或 BigDecimal
之类的东西,其行为与 String
比较,因为比较两个 BigDecimal
值是否相等最终可能是 O(n)(其中 n 是 BigDecimal
的长度,而不是它们的任何一个数值)。
一般来说,我们只在 n 可以上升到非常大的值时才使用 big-O 表示法,因为 big-O 表示法描述了执行时间如何随着输入的增长而增长。例如,在对列表进行排序时,大多数最好的算法都按 O(n log n)
排序——这意味着, 仅 意味着,当列表足够长时,它的时间排序所需的时间与 n log n
成正比。当列表不够长时,其他因素(例如,您的算法可能需要分配额外 space 的任何时间)会变得很重要,甚至可能会占用 运行ning 时间。
对于 JavaScript 个字符串,n
确实可以任意大*,所以我们说比较需要 O(n)
时间。但是对于 JavaScript 个数字(即 IEEE 754 double-precision floating point numbers),n
的最大上限为 64 - 符号位为 1,指数为 11,有效数字为 53**。正因为如此,我们确切地知道进行数字比较可能需要多长时间,并且我们拥有的用于比较确切大小的数字的最佳系统或多或少 运行 相同,无论这 64 个中有多少每个数字实际具有的数字 - 因此,比较 JavaScript 中的这些数字被认为是 O(1)
.
*从技术上讲,有一个上限,因为 RAM 可以 运行。但是,该语言没有指定字符串的最大大小,并且字符串比较的 O(n)
部分在发生之前就占据了执行时间。
**顺便说一句,这确实意味着 JavaScript 中的数字不能无限增加。过了某个点,他们开始丢弃更小的数字(例如,2^53 以上的数字只能是偶数,2^54 以上的数字只能被 4 整除),当数字足够大时,它会四舍五入到无穷远。反之,如果你把一个数一遍又一遍地除以使其无限小,它最终会四舍五入为零。