std:set_intersection
的复杂性。当我回答时,我提到了
O(N + M)
等于:
O(MAX(N,M))
我被告知这是不正确的。我没有成功地试图表现出对等:
O(0.5 *(n + m))≤O(max(n,m))≤O(n + m)
我的问题是:我真的不对吗?
答案 0 :(得分:10)
对于所有 m , n ≥0,max( m , n )≤< O 中的em> m + n →max( m , n ) m + n ), m + n ≤2max( m , n )→ m + n O (最大值( m , n ))。
因此 O (max( m , n ))= O ( m + n )。
ADDENDUM :如果 f 属于 O ( m + n ),那么常数 D &gt; 0存在, f ( n , m )&lt; D *( m + n ) m 和 n 足够大。因此 f ( n , m )&lt; 2 D * max( m , n )和 O ( m + n )必须是 O 的子集(max( m , n ))。 O 的证明(max( m , n ))是 O 的子集( m + n )类似。
答案 1 :(得分:9)
你完全正确的O(n + m)等于O(max(n,m)),更准确地说我们可以证明Θ(n + m)=Θ(max(n,m)更加严密,证明你的判断。数学证明(对于大O和Θ)非常简单,易于理解。因为我们有mathematical proof
这是一种说话的方式,但在一种更明确,更严格的方式doesn't leaves any ambiguity
。
虽然你(错误地)告诉我这是不正确的,因为如果我们想要非常精确,这不是合适的 - 数学方式来表达max(m,n)的顺序与m + n相同。您使用了“等于”这个词来指代大O符号,但是大O符号的定义是什么?
它被称为
Sets
。说max(n + m)属于O(m + n)是 最正确的方式,反之亦然m + n属于O(max(m,n))。在大O. 通常使用和表示m + n = O(max(n,m))的符号。
引起的问题是你没有尝试引用函数的顺序,比如f是O(g),但是你试图比较集合O(f)和O(g)。但是证明两个无限集是平等并不容易(这可能会使面试官感到困惑)。
我们可以说当包含相同的元素时,集合A和B是相同的(或相等)(我们不会尝试比较,而是引用它们包含的元素,因此它们必须是有限)。在谈论Big O Sets时,甚至不能轻易应用识别。
F的大O用于表示我们正在谈论的是Set 包含顺序大于或等于F的所有函数 功能有吗??
Infinite since F+c is contained and c can take infinite values.
你怎么能说两个不同的集合相同(或相等) 无限,这不是那么简单。
So I understand what you are thinking that n+m and max(n,m) have same
order but **the right way to express that** is by saying n+m is
O(max(n,m)) and max(n,m)is O(m+n) ( O(m+n) is equal to O(max(m,n))
may better requires a proof).
还有一件事,我们说这些函数具有相同的顺序,这在数学上是正确的,但在尝试优化算法时,您可能需要考虑一些较低阶因子,那么它们可能会给您略微不同的结果但渐近行为被证明是相同的。
<强> 结论 强>
正如您可以阅读wikipedia(以及所有大学或每本算法书中的所有课程)Big O /θ/Ω/ω/ο标记helps us compare functions
并找到它们的增长顺序而不是功能集,这就是你被告知错了的原因。虽然很容易说O(n ^ 2)是O(n)的子集,但如果两个集合相同则很难比较无穷大。康托尔致力于对无限集进行分类,例如我们知道自然数是无数的,而实数是无数无数的,所以即使两者都是无穷大,实数也不仅仅是自然数。在尝试对无限集进行排序和分类时,它变得非常复杂,这将更多地是数学研究,而不是比较函数的方法。
<强>更新强>
事实证明你可以简单地证明O(n + m)等于O(max(n,m)):
对于属于O(n + m)的每个函数F,这意味着有常数c和k这样:
F <= c(n+m) for every n>=k and m>=k
然后也站着:
F <= c(n+m)<= 2c*max(n,m)
因此F属于O(max(n,m)),因此O(m + n)是O的子集(max(n,m))。 现在考虑F属于O(max(n,m))然后有常数c和k这样:
F <= c*max(n+m) for every n>=k and m>=k
我们也有:
F <= c*max(n+m)<=2c(m+n) for every n>=k and m>=k
因此根据定义存在c'= 2c且具有相同的k:F是O(m + n)并且因此O(max(n,m))是O(n + m)的子集。因为我们证明O(m + n)是O(max(n,m))的子集,我们证明 O(max(m,n))和O(m + n)相等 <强大>这个数学证明证明你毫无疑问是完全正确的。
最后请注意,证明m + n为O(max(n,m))且max(n,m)为O(m + n)并不能立即证明集合相等(我们需要一个证明),因为你的说法只是证明函数具有相同的顺序,但我们没有检查集合。虽然很容易看到(一般情况下)如果 f是O(g)而g是O(F)那么你可以很容易证明那个大O设置相等< / strong>就像我们在前一段中所做的那样。
答案 2 :(得分:5)
我们通过严格的Big-O分析表明您确实是正确的,在您的分析中给出了一个可能的增长参数选择。然而,这并不一定意味着访谈者的观点是不正确的,而是他/她对增长参数的选择不同。然而,他/她提示您的答案完全不正确是值得怀疑的:您可能只是使用两种稍微不同的方法来分析std::set_intersection
的渐近复杂性,这两种方法都导致了算法运行的普遍共识在线性时间。
让我们先看一下cppreference上的std::set_intersection
的引用(强调我的)
参数的
first1
,last1
- 要检查的第一个元素范围
first2
,last2
- 要检查的第二个元素范围复杂性
最多
2·(N1+N2-1)
比较,N1 = std::distance(first1, last1) N2 = std::distance(first2, last2)
std::distance
本身就是线性的(最坏情况:没有随机访问)
std::distance
...
返回
first
和last
之间的元素数。
我们将简要回顾一下Big-O符号的基本原理。
我们松散地说明f
中的函数或算法O(g(n))
的定义(挑剔,O(g(n))
是函数集,因此{ {1}},而不是通常误用的f ∈ O(...)
)。
如果<{em>}
f(n) ∈ O(...)
中的函数f
,那么O(g(n))
就是上层 绑定在c · g(n)
上,用于某些非负常数f(n)
,以便c
保持,表示足够大f(n) ≤ c · g(n)
(即n
表示常量n ≥ n0
)。
因此,要显示n0
,我们需要找到一组(非负)常量f ∈ O(g(n))
来实现
(c, n0)
但是,我们注意到,此设置并非唯一;找到常量f(n) ≤ c · g(n), for all n ≥ n0, (+)
使得(+)成立的问题是退化。实际上,如果存在任何这样的常数对,那么将存在无限量的不同这样的对。
我们继续对(c, n0)
进行Big-O分析,基于已知的最坏情况下的算法比较(我们将这样的比较视为基本操作)。
std::set_intersection
示例现在考虑两个元素范围,例如set_intersection
和range1
,并假设这两个范围中包含的元素数分别为range2
和m
。
n
作为选择的参数:我们仍然会得出结论:k = m+n
具有线性时间复杂度,而是std::set_intersection
(其中是k
不是 m+n
),而是max(m, n)
和m
中的最大值。这些只是我们在继续进行Big-O表示法/渐近分析之前自由选择设置的前提条件,并且很可能访问者倾向于选择使用n
作为复杂性进行分析。增长参数而不是其两个组成部分中的最大值。现在,从上面我们知道,在最坏的情况下,k
将运行std::set_intersection
比较/基本操作。让
2 · (m + n - 1)
由于目标是根据Big-O(上限)找到渐近复杂度的表达式,我们可以在不失一般性的情况下假设h(n, m) = 2 · (m + n - 1)
和定义
n > m
我们继续用Big-O表示法分析f(n) = 2 · (n + n - 1) = 4n - 2 > h(n,m) (*)
的渐近复杂性。让
f(n)
并注意
g(n) = n
现在(选择)让f(n) = 4n - 2 < 4n = 4 · g(n)
和c = 4
,我们可以陈述以下事实:
n0 = 1
鉴于f(n) < 4 · g(n) = c · g(n), for all n ≥ n0, (**)
,我们从(**)
知道我们现在已经证明
(+)
此外,由于`(*)成立,自然
f ∈ O(g(n)) = O(n)
成立。
如果我们改变我们的初始假设并假设h ∈ O(g(n)) = O(n), assuming n > m (i)
,那么重新跟踪上面的分析将反过来产生类似的结果
m > n
因此,给定两个范围h ∈ O(g(m)) = O(m), assuming m > n (ii)
和range1
分别持有range2
和m
元素,我们已经证明了{{1}的渐近复杂性应用两个这两个范围的确是
n
我们选择了最大的std::set_intersection
和O(max(m, n))
作为我们分析增长的参数。
然而,当谈到Big-O表示法时,这并不是真正有效的注释(至少不常见)。当我们使用Big-O表示法来描述某些算法或函数的渐近复杂性时,我们就某个单一的增长参数(不是其中两个)这样做。
我们可以在不失一般性的情况下假设m
描述范围内具有最多元素的元素数量,而不是回答复杂性为n
,并且假设这个假设,只是状态而不是O(max(m, n))
的渐近复杂度的上限是n
(线性时间)。
关于面试反馈的猜测:如上所述,面试官可能只是坚定地认为Big-O符号/渐近分析应该基于{{1} }作为增长的参数而不是其两个组成部分中的最大部分。当然,另一种可能性是,面试官只是混淆地询问std::set_intersection
的实际比较次数的最坏情况,同时将其与Big-O表示法和渐近式的单独事项相混淆。复杂性。
最后请注意,对O(n)
的最坏情况复杂性的分析并不代表通常研究的非有序集交集问题:前者适用于已经排序的范围(参见Boost&#引用) 39;下面的k = m+n
:std::set_intersection
的起源,而在后者中,我们研究非有序集合的交集的计算。
描述的
std::set_intersect
构造一个排序范围即交集 已排序范围set_intersection
和std::set_intersection
。返回值是 输出范围的结束。
作为后者的示例,Python的set_intersection
set方法适用于非有序集合,并应用于集合rng1
和rng2
,it has an average case and a worst-case complexity of O(min(len(s), len(t))
and O(len(s) * len(t))
, 分别。这种实现中平均和最差情况之间的巨大差异源于这样一个事实,即基于散列的解决方案在实践中通常运行良好,但对于某些应用,理论上可能具有非常差的最坏情况性能。
有关后一问题的其他详细信息,请参阅例如