【问题标题】:Log base 3 time search algorithm对数基3时间搜索算法
【发布时间】:2017-01-27 00:29:32
【问题描述】:

完全披露这是一个家庭作业问题,因此,我不会要求具体的解决方案,只是对一些问题的一般性答案。问题正文如下:

给定一个 T 类型的排序数组,它必须实现 Comparable 接口, 编写一个在数组中查找特定元素的 Java 泛型方法 并返回它,如果没有找到则返回 null。请注意,您的算法 必须在 log base 3 time 中运行更坏的情况,log base 2(二进制搜索)解决方案 将不会获得任何信用

还有一个额外的规定是算法必须是迭代的,而不是递归的。

1) 所以我的第一个问题是,我如何确定算法以对数基数 3 而非对数基数 2 运行?两者仅相差一个常数因子,因此即使我分析算法的结构,我怎么知道我是在 Log3(n) 时间运行,还是仅在 (~0.63)(Log2(n)) 运行时间?这还重要吗?

2) 我的印象是二进制搜索或多或少是搜索排序数组的标准快速算法,因此除了二进制和线性搜索之外,还有哪些其他搜索算法可以从中寻找灵感?

3) 有没有我遗漏的东西,一些允许比标准二进制搜索更快地搜索数组的规定?

非常感谢任何帮助,如果这个问题相当具体,很抱歉,但我认为它的某些部分可以普遍适用。

【问题讨论】:

  • @dhS:不正确,我们在这里是为了解决恰当的问题,这些问题可以增强 SO 所追求的问题和答案的知识库。恕我直言,这个问题符合这个要求,因此我赞成。
  • 不清楚你在问什么,因为你没有具体说明你的意思。
  • 这只是我,或者任务意味着使用以 3 为底的搜索而不是 2 ......所以分成三份而不是索引的一半......这是 T(log3(n)) 但需要 2x @987654323 @ 每次迭代而不是单个迭代。还有其他搜索然后二进制在那里你可以例如看看这个How approximation search works
  • 如您所见,分配没有意义。您可能希望编写一个三元搜索,它不会比二分搜索快。但是,如果计算机科学对你很重要,你应该弄清楚你是否真的被一个实际上并不了解任何计算机科学的人教过,以及你将要做什么。
  • 请告诉我们问题作者的想法!如果您已经知道,那就是。

标签: arrays algorithm search


【解决方案1】:
  1. 没错,O(log(n)) 不关心 log 是 base 2 还是 base 3。

  2. 现在假设任务是使用不超过一定数量的比较。我们可以证明,在最坏情况下不超过 log3(N) 次比较且没有常数因子来解决它是不可能的。对于初学者,如果数组包含三个元素,则不可能找到元素仅在一次比较后报告它丢失。同样,对于一个九元素数组,两次比较显然是不够的。人们可以使用鸽巢原理和一些注意来证明大 N 也是不可能的,尽管我承认我没能在几分钟内做出 short 证明。
    我会为大 N 尝试的证明想法是这样的(虽然细节变得乏味)。如果所有数组元素都是不同的,并且与我们搜索的 X 不同,在问 K 个问题后,我们只得到 K 位信息,因此可以区分不超过 2K 个数组段X 可能在哪里。然后,存在一段一定长度(比如3个元素)的段,其中有一个我们没有比较但仍然可以等于X的元素,将其改为X,我们得到一个反例数组。

【讨论】:

  • We can prove that [deciding occurrence in <=] log3(N) comparisons with no constant factor in the worst case is impossible. For starters, if the array has three elements, it is impossible to find the element or report it missing after just one comparison. Similarly, for a nine-element array, … 形式上,在某个阈值输入值 x₀ 之上不超过限制函数就足够了。此外,这似乎假设了恰好两个输入值(已同意)和两个可能的结果之间的比较——存在带有 的机器实现(和编程语言扭曲)
  • @greybeard 你是对的,对于 Java Comparable,有三种可能的结果。然而,根据我的假设(所有数组元素都是不同的,也不同于 X),只剩下两个结果。我们后来打破了这个假设,但只针对一个我们从未将 X 与之比较的元素。
【解决方案2】:

二分查找的主要好处是简单;给定一个具有端点xy 的子数组,您进行一次比较和一次中点计算以确定下一个子数组([x,(x+y)/2][(x+y)/2, y]

使用三元搜索,您需要进行两次比较和两次中点计算,以确定接下来要搜索[x, (x+y)/3][(x+y)/3, 2*(x+y)/3][2*(x+y)/3, y] 中的哪一个。您可以更快地缩小范围,但要付出更多的工作。

渐近地,它们都是O(lg n)。二进制搜索之所以获胜,是因为它很简单,还因为您假设访问下一个键并不比用于确定它的比较昂贵。对于连续存储在内存中的数组,这最终是正确的,因为最终子数组适合主内存,然后完全适合每个连续的缓存级别。 (即数组具有良好的空间局部性)。

对于存储在数组中的数据——比如说,在树中——获取下一个节点的成本可能远远超过确定哪个节点所需的比较成本要获取的节点。在这种情况下,您希望在每次提取时将尽可能多的有用数据加载到内存中。在这种情况下,三叉树可能并不比二叉树好多少,但是像B-tree 这样的东西,其中每个内部节点的大小(它决定了节点中可以存储多少键)被调整到大小本地缓存,可以大大减少实际运行时间,即使它仍然像二进制搜索一样 O(lg(n))。

【讨论】:

    【解决方案3】:

    我相信,如果您做的事情比仅仅在每次迭代中将集合减半更聪明,那么您可以做得比简单的二分搜索更好。

    您可以改为选择 pivot,方法是检查更多地被视为单调 函数 而不是值序列的输入。

    布伦特算法使用这种方法,可以让你更快收敛。

    https://en.wikipedia.org/wiki/Brent%27s_method

    如果这是我的任务,我的解决方案会以此为基础。

    【讨论】:

    • @Downvoter,如果您对我选择的算法有疑虑,请告诉我。
    • 布伦特的方法读起来很有趣,但它与这个问题的关系不太可能(这可能解释了反对意见)。该方法旨在加快对表现良好的连续函数的搜索。一个问题是排序数组不是连续的。这可以通过适当的舍入来克服。另一个是最坏情况的时间复杂度是O(log^2(n)),不如二分查找。最后,以 3 为底的对数似乎与 Brent 的方法无关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-20
    • 2011-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多