【问题标题】:Quadratic Probing : in which scenario in real life will it be O(n) complexity二次探测:在现实生活中的哪种情况下会是 O(n) 复杂度
【发布时间】:2021-08-19 06:49:09
【问题描述】:

我正在学习数据结构和算法。我被教导几乎所有 hashmap 操作几乎都是 O(1)。这是一种执行二次探测分辨率的方法,它返回所需对象的位置。

private int findPos( Object x )
{
    int offset = 1;
    int currentPos = myhash( x ); //Using quadratic probing 
    
    while( array[ currentPos ] != null &&
            !array[ currentPos ].element.equals( x ) )
    {
        currentPos += offset;  // Compute ith probe
        offset += 2;
        if( currentPos >= array.length )
            currentPos -= array.length;
    }
    
    return currentPos;
}

我正在做作业,我必须找出这种方法的复杂性。这是场景:

该数组表示一个英语词典,其中每个元素都是其中的一个单词。这个作业的答案是 O(1) 复杂度。但我想知道为什么不是 O(n) 复杂度,因为里面有很多元素(大约 171,476 个字)会导致很多冲突。换句话说,我怎么能确定,在这种情况下,这个方法总是 O(1) ?

编辑 我的作业问题是:在最坏的情况场景中,这种方法的复杂性是多少。还是 O(1) 吗?

【问题讨论】:

  • 对于每个元素,有两种情况:要么有碰撞,要么没有。考虑这两种情况的概率来计算每个元素的估计插入复杂度。最后,对n 值取平均值,以找到平均插入时间。
  • 哈希表上的大多数操作不是 O(1),而且正如你所说的 O(n) 或更糟。他们的预期运行时间是 O(1)。
  • “里面有很多元素(约171,476字),会导致很多碰撞” - 碰撞的几率不取决于数量元素,它取决于负载系数。只要底层数组有足够的空间容纳元素,那么灾难性碰撞(即导致 O(n) 行为的一系列碰撞)就极不可能发生。
  • @ldog 所以,正如你所说,它们是 O(1) 预期时间,而不是 O(1) 最坏情况时间。就像快速排序是 O(n log n) 预期时间但 O(n^2) 最坏情况时间。除非我们另有说明,否则没有默认假设我们正在测量最坏的情况。

标签: algorithm data-structures time-complexity big-o


【解决方案1】:

非最坏情况.... 它是 O(1) 而不是 O(n),因为搜索时间不取决于数组的大小。是的,你会得到更多元素的更多冲突,但平均每次搜索不会有更多的冲突,因为数组的大小会随着 n 的增加而增加。

编辑:由于问题规定数据是来自英语的单词,因此 n 的最大可能大小有一个限制,这意味着您实际上可以保证哈希函数不会发生冲突,这将使答案 O(1)即使在最坏的情况下。

【讨论】:

【解决方案2】:

在最坏的情况下,这种方法的复杂性是多少。即使在最坏的情况下,它仍然是 O(1) 吗?

要回答您的问题,您确实必须定义最坏情况的含义。哈希表的实现很大程度上取决于两个因素:您打算存储在哈希表中的数据的(概率)分布,以及您定义的哈希函数。了解这两个因素,您可以进行平均案例分析。如果没有这两条信息,您可以假设最坏的情况,例如:

  • 您将始终获得相同的值来存储数字“1”
  • 您的哈希函数是 f(x) = x

在这种情况下,如果您对存储桶使用链表,那么最坏的情况确实是 O(n)。

但是,如果您有更多信息,例如您的数据遵循均匀分布并且您使用合理的散列函数,则可以在 平均 的情况下显示大多数操作应该是 O(1) .在上一句中,描述符“平均”是通过对数据遵循的概率分布进行平均来精确定义的。


我不清楚您需要多详细的分析,但通常使用开放寻址(不管是哪个)平均运行时间的分析如下。

a = n / N 是哈希表的负载因子,其中n 是存储的元素数,N 是桶数。假设没有聚类问题(由于数据或散列函数)并且所有探测的可能性相同(证明二次探测的正确性是分开的),那么您可以断言

P(probe hits occupied bucket) = a
P(probe hits unoccupied bucket) = 1-a
P(probe hits unoccupied bucket in 2 steps) = a (1-a)
P(probe hits unoccupied bucket in k steps) = a^{k-1} (1-a)

因此探测的平均运行时间为

E(number of steps in probe) = \sum {for k = 0 to m} k a^{k-1}(1-a)
                            <=\sum {for k = 0 to infty} k a^{k-1}(1-a)
                            = (1-a) / (1-a)^2
                            = 1 / (1 - a)

其中无限和由arithmetico-geometric series 给出,m 是探测步骤数的某个上限(取决于探测技术,通常需要单独证明探测技术以有限的方式终止#步骤m。)

如果我们保持一个合理的负载系数a,比如a = n / N = .5,那么

E(number of steps in probe) <= 1 / (1-.5) = 2

因此E(number of steps in probe) 是 O(1)。

整个分析的关键在于能够识别出无限和有一个很好的收敛形式。

回顾一下,我上面给出的证明在一般情况下是成立的,但是你正在使用的探测技术需要以下条件(可能需要单独证明):

  • 探测技术根据命中未占用/已占用存储桶的负载因子产生统一概率(即P(probe hits occuppied bucket) = a
  • 探测技术以有限的步骤结束

根据您的分析必须有多详细,您可能必须证明二次探测的这两个属性才能完成证明。 (请注意,仅当负载因子为

【讨论】:

  • 因此,为了澄清我的示例,数组上使用的数据是字典中的单词。答案是 O(1)(我的作业),因为我的数据(字典中的单词)的分布是统一的
  • 是否有更多关于使用的哈希函数的信息?它会对性能分析产生非常大的影响。
  • 它使用 Java 中的 hashCode() 方法,该方法使用了 Horner 算法,我猜这是一个很好的哈希函数
  • 好的,我已经更新了答案以反映您迄今为止提供的所有信息。取决于您实际需要显示 YMMV 的内容,但我至少概述了常量运行时的典型证明的框架。
  • 所以,因为我使用 0.5 的二次探测,它解决了“产生均匀概率”和“在有限数量的步骤中终止”的问题。那么,在平均运行时,即使在更坏的情况下(在您的示例中收敛到 2),方法 findPos() 也不可能是 O(n),它是正确的吗?
猜你喜欢
  • 2020-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-07
  • 2019-04-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多