在任何情况下,您都更喜欢O(log n)
时间复杂O(1)
度而不是时间复杂度吗?还是O(n)
要O(log n)
?
你有什么例子吗?
在任何情况下,您都更喜欢O(log n)
时间复杂O(1)
度而不是时间复杂度吗?还是O(n)
要O(log n)
?
你有什么例子吗?
Answers:
可能有很多原因会优先考虑O时间复杂度较高而不是较低的算法:
10^5
从big-O的角度来看,执行in的算法比1/10^5 * log(n)
(O(1)
vs O(log(n)
)n
更好,但是在大多数情况下,第一个算法的执行效果更好。例如,矩阵乘法的最佳复杂度是,O(n^2.373)
但是常数是如此之高,以至于(据我所知)没有计算库使用它。O(n*log(n))
还是O(n^2)
算法都无关紧要。O(log log N)
时间复杂度找到一个项目,但也有它找到了在同一二叉树O(log n)
。即使是数量巨大n = 10^20
的差异也可以忽略不计。O(n^2)
并需要O(n^2)
内存的算法。当n不是很大时,在O(n^3)
时间和O(1)
空间上可能更可取。问题是您可以等待很长时间,但高度怀疑您是否可以找到足够大的RAM来与算法配合使用O(n^2)
,比quicksort或mergesort差,但是作为一种在线算法,它可以在接收到值(作为用户输入)时有效地对值列表进行排序,而大多数其他算法只能有效地进行操作在值的完整列表上。总是存在隐藏常数,在O(log n)算法中可以更低。因此,对于实际数据,它可以在实践中更快地工作。
还存在空间问题(例如,在烤面包机上运行)。
开发人员还担心时间-O(log n)可能易于实现和验证1000倍。
lg n
是如此,如此,如此之k
大n
,以至于大多数操作永远不会注意到差异。
令我惊讶的是,没有人提到内存绑定应用程序。
可能由于其复杂度(即O(1)< O(log n))或由于复杂度前面的常数较小(即2 n 2 <6 n 2)而导致浮点运算较少的算法。无论如何,如果较低的FLOP算法具有更多的内存限制,您可能仍然更喜欢FLOP较高的算法。
我所说的“内存绑定”是指您经常访问不断超出缓存的数据。为了获取此数据,必须先将实际存储空间中的内存拉入缓存,然后才能对其执行操作。此提取步骤通常很慢-比操作本身要慢得多。
因此,如果您的算法需要更多的操作(但是这些操作是对已在缓存中的数据执行的,因此不需要提取),那么它仍然会以更少的操作胜过您的算法(必须在超出次数的情况下执行) -cache数据(因此需要提取))。
O(logn)
过O(1)
。您可以很容易地想到这样一种情况,即在所有可行的情况下n
,即使内存复杂度较高,运行于内存的较少的应用程序也将以更快的运行时间运行。
在关注数据安全性的情况下,如果复杂度更高的算法对定时攻击具有更好的抵抗力,则复杂度更高的算法可能会比复杂度较低的算法更可取。
(n mod 5) + 1
与之成正比,那么它仍将O(1)
揭示有关的信息n
。因此,即使运行时渐近(甚至可能在实践中)较慢,但更复杂,运行时更平滑的算法可能更可取。
Alistra钉牢了它,但是没有提供任何示例,所以我会的。
您拥有商店销售的10,000个UPC代码的列表。10位UPC,价格整数(便士价格)和30个字符的收据说明。
O(log N)方法:您有一个排序列表。如果是ASCII,则为44个字节;如果为Unicode,则为84个字节。或者,将UPC视为int64,则得到42和72个字节。10,000条记录-在最高的情况下,您所需要的存储空间不到1兆字节。
O(1)方法:不要存储UPC,而是将其用作数组的入口。在最低的情况下,您需要使用近三分之一的存储空间。
您使用哪种方法取决于您的硬件。在大多数合理的现代配置中,您将使用log N方法。我可以想象第二种方法是正确的答案,如果由于某种原因您正在RAM严重不足但您拥有大量海量存储的环境中运行。磁盘上三分之一的TB没什么大不了的,将数据放在磁盘的一个探针中是值得的。简单的二进制方法平均需要13。(但是请注意,通过将您的键聚类,您可以将其降低到保证的3次读取,实际上,您将缓存第一个。)
malloc(search_space_size)
,只需说出并写出返回的内容就可以了。
考虑一棵红黑树。它可以访问,搜索,插入和删除O(log n)
。与具有访问权限的数组进行比较,O(1)
其余操作为O(n)
。
因此,如果给我们一个比我们访问更频繁地插入,删除或搜索的应用程序,并且仅在这两种结构之间进行选择,我们将更喜欢红黑树。在这种情况下,您可能会说我们更喜欢红黑树的O(log n)
访问时间。
为什么?因为访问不是我们最关心的问题。我们正在做出权衡:我们应用程序的性能在很大程度上受到除此因素以外的其他因素的影响。由于我们通过优化其他算法获得了巨大收益,因此我们可以让这种特定算法遭受性能损失。
因此,您的问题的答案很简单:当算法的增长率不是我们要优化的速度时,当我们要优化其他事物时。所有其他答案都是这种情况的特例。有时我们会优化其他操作的运行时间。有时我们会优化内存。有时我们会针对安全性进行优化。有时我们会优化可维护性。有时我们会为开发时间进行优化。当您知道算法的增长速度对运行时间没有最大的影响时,即使最重要的最低常量也正在针对运行时间进行优化。(如果您的数据集超出此范围,则您将针对算法的增长率进行优化,因为它最终将控制该常数。)一切都有代价,并且在许多情况下,我们将较高增长率的代价换为优化其他内容的算法。
O(log n)
“红黑树”的“访问”和“搜索”?5
在数组的位置2 插入[1, 2, 1, 4]
将导致[1, 2, 5, 1 4]
(元素4
将使索引从3更新为4)。如何在O(log n)
引用为“排序列表”的“红黑树”中获得这种行为?
是。
在实际情况下,我们对使用短字符串键和长字符串键进行表查找进行了一些测试。
我们使用了std::map
a std::unordered_map
和a ,其中的哈希值在字符串的长度上最多采样10次(我们的键通常像guid一样,所以这很不错),而哈希值则对每个字符进行采样(理论上减少了冲突),我们进行==
比较的未排序向量,以及(如果我没记错的话)我们也存储哈希的未排序向量,首先比较哈希,然后比较字符。
这些算法的范围从O(1)
(unordered_map)到O(n)
(线性搜索)。
对于中等大小的N,通常O(n)胜过O(1)。我们怀疑这是因为基于节点的容器需要我们的计算机在内存中跳得更多,而基于线性的容器却没有。
O(lg n)
两者之间存在。我不记得是怎么回事。
性能差异不是很大,在较大的数据集上,基于哈希的数据集的性能要好得多。因此,我们坚持使用基于散列的无序映射。
实际上,对于合理大小的n,O(lg n)
为O(1)
。如果您的计算机在表中仅可容纳40亿个条目,则该O(lg n)
范围的上限为32
。(lg(2 ^ 32)= 32)(在计算机科学中,lg是基于日志2的简写)。
实际上,lg(n)算法比O(1)算法要慢,这不是因为对数增长因子,而是因为lg(n)部分通常意味着算法存在一定程度的复杂性,并且该复杂性会增加大于lg(n)项中任何“增长”的常数因子。
但是,复杂的O(1)算法(例如哈希映射)可以很容易地具有相似或更大的常数因子。
并行执行算法的可能性。
我不知道是否有关于类O(log n)
和的示例O(1)
,但是对于某些问题,当算法更易于并行执行时,您选择了具有较高复杂性类的算法。
有些算法无法并行化,但是复杂度很低。考虑另一种算法,它可以达到相同的结果,并且可以很容易地并行化,但是具有更高的复杂度等级。在一台机器上执行时,第二种算法较慢,但在多台机器上执行时,实际执行时间越来越短,而第一种算法无法加快速度。
假设您要在嵌入式系统上实现黑名单,其中可能会将0到1,000,000之间的数字列入黑名单。剩下两个可能的选择:
访问该位集将保证有恒定的访问权限。就时间复杂度而言,它是最佳的。从理论和实践角度来看(它都是O(1),常量开销都非常低)。
尽管如此,您可能仍希望使用第二种解决方案。尤其是如果您希望将列入黑名单的整数的数量很少,因为这样可以提高内存效率。
即使您不为内存不足的嵌入式系统而开发,我也可以将1,000,000的任意限制增加到1,000,000,000,000,并进行相同的论证。那么该位集将需要大约125G的内存。确保O(1)的最坏情况复杂性可能不会说服您的老板为您提供如此强大的服务器。
在这里,我非常希望使用二进制搜索(O(log n))或二进制树(O(log n)),而不是O(1)位集。而且,在最坏情况下复杂度为O(n)的哈希表在实践中可能会击败所有哈希表。
在随机矩阵的所有行上进行快速随机加权选择是一个示例,其中算法O(m)的算法比复杂度为O(log(m))的算法快,但算法m
又不太大。
一个更笼统的问题是,即使在趋向于无穷大的情况下,是否存在人们更喜欢O(f(n))
算法而不是O(g(n))
算法的情况。正如其他人已经提到的,在和的情况下,答案显然是“是” 。即使是多项式但是指数的情况,有时也可以。一个著名且重要的例子是用于解决线性规划问题的单纯形算法。在1970年代被证明是。因此,其最坏情况的行为是不可行的。但是,即使对于具有成千上万个变量和约束的实际问题,其平均情况下的行为也非常好。在1980年代,多项式时间算法(例如g(n) << f(n)
n
f(n) = log(n)
g(n) = 1
f(n)
g(n)
O(2^n)
人们发现了用于线性规划的Karmarkar的内点算法(),但是30年后,单纯形算法似乎仍然是首选算法(除了某些非常大的问题)。这是因为显而易见的原因是平均情况下的行为通常比较差情况下的行为更重要,而且还出于更细微的原因,在某种意义上,单纯形算法更具参考价值(例如,更容易提取敏感度信息)。
投入2美分:
当算法在特定的硬件环境上运行时,有时会选择复杂度较差的算法来代替更好的算法。假设我们的O(1)算法非顺序访问非常大且固定大小的数组的每个元素来解决我们的问题。然后将该阵列放在机械硬盘驱动器或磁带上。
在这种情况下,O(logn)算法(假设它顺序访问磁盘)变得更受欢迎。
简而言之:因为系数(与设置,存储和该步骤的执行时间相关的成本)对于big-O较小的问题可能比较大的问题大得多。Big-O仅是算法可伸缩性的度量。
考虑一下《黑客词典》中的以下示例,提出了一种基于量子力学的多重世界解释的排序算法:
- 使用量子过程随机排列阵列,
- 如果数组未排序,请销毁Universe。
- 现在将对所有剩余的Universe进行排序(包括您所在的Universe)。
(来源:http : //catb.org/~esr/jargon/html/B/bogo-sort.html)
请注意,此算法的big-O为O(n)
,它击败了迄今为止在通用商品上所有已知的排序算法。线性步长的系数也非常低(因为它只是一个比较,不是交换,而是线性完成的)。实际上,可以使用类似的算法来解决多项式时间内NP和共NP中的任何问题,因为可以使用量子过程生成每个可能的解(或没有解的可能证明),然后在多项式时间。
但是,在大多数情况下,我们可能不想冒多个世界可能不正确的风险,更不用说实施步骤2的行为仍然“留给读者练习”。
在任何时候,当n有界且O(1)算法的常数乘数高于log(n)的界。 例如,将值存储在哈希集中为O(1),但可能需要对哈希函数进行昂贵的计算。如果可以比较数据项(相对于某个顺序),并且对n的限制是log n显着小于任何一项的哈希计算,那么存储在平衡二叉树中可能比存储在二叉树中更快。哈希集。
再加上已经很好的答案。一个实际的例子是postgres数据库中的Hash索引与B-tree索引。
哈希索引形成哈希表索引以访问磁盘上的数据,而名称顾名思义的btree使用Btree数据结构。
在Big-O时间内,它们是O(1)vs O(logN)。
目前在postgres中不建议使用散列索引,因为在现实生活中,尤其是在数据库系统中,要实现无冲突的散列非常困难(可能导致O(N)最坏情况的复杂性),因此,制作起来更加困难它们崩溃安全(称为预写日志记录-postgres中的WAL)。
在这种情况下要进行权衡,因为O(logN)对于索引来说已经足够好了,而O(1)的实现非常困难,并且时间差实际上并不重要。
对于安全应用程序,通常会遇到这种情况,我们希望设计算法故意过慢的问题,以阻止某人过快地获得问题的答案。
这是我脑中浮现的几个例子。
O(2^n)
,时间n
是关键的比特长度(这是蛮力)。在CS的其他地方,“快速排序” O(n^2)
在最坏的情况下是,但在一般情况下是O(n*log(n))
。因此,在分析算法效率时,您并不是唯一关心“ Big O”分析的人。
O(log n)
算法而不是O(1)
算法...