【问题标题】:Find existence of number in a sorted list in constant time? (Interview question)在恒定时间内找到排序列表中数字的存在? (面试题)
【发布时间】:2010-06-16 14:48:39
【问题描述】:

我正在为即将到来的面试而学习,并且已经多次遇到这个问题(逐字逐句)

在 N 个数字的排序列表中查找或确定一个数字不存在,其中数字范围超过 M、M >> N 和 N 大到足以跨越多个磁盘。击败 O(log n) 的算法;恒定时间算法的加分。

首先,我不确定这是否是一个真正有解决方案的问题。我和我的同事已经对这个问题进行了数周的思考,但它似乎不正确(当然,仅仅因为我们想不出解决方案并不意味着没有解决方案)。我会问面试官的几个问题是:

  • 排序列表中是否有重复项?
  • 磁盘数和N有什么关系?

我考虑的一种方法是对每个磁盘的最小值/最大值进行二进制搜索,以确定应该保存该数字的磁盘,如果存在,然后在磁盘本身上进行二进制搜索。当然,如果磁盘数量很大并且您还有一个排序的磁盘列表,这只是一个数量级的加速。我认为这会产生某种 O(log log n) 时间。

至于 M >> N 提示,也许如果您知道磁盘上有多少个数字以及范围是多少,您有时可以使用鸽巢原理排除某些情况,但我不能找出一个数量级的改进。

另外,“恒定时间算法的奖励积分”让我有点怀疑。

对此问题有任何想法、解决方案或相关历史吗?

【问题讨论】:

  • 我仍然对此感到好奇,我想看看另外几个答案。
  • 为什么不使用散列(即分布式散列表)?
  • 哎呀,这可以工作,给定一个快速的散列,以及减少空间浪费的机制之一,但分布式散列表并不是微不足道的。当然,我不想在 40 分钟内写在白板上。不过,从根本上来说,这并不是一个坏主意。老实说,我可能只会大喊 Judy Arrays 就跑。
  • 但是,如果它是一个哈希表,它就不是一个排序列表。另外,请注意,“分布式哈希表”实际上是指与您需要的完全不同的数据结构 - 您需要的只是基于磁盘的哈希表。

标签: algorithm


【解决方案1】:

由于问题没有说明数字以哪种格式存储,您可以告诉面试官您将假设数字以物理方式存储。例如,每个数字都可以写在一张卡片上,并且每张卡片归一个人所有。

N 大到足以跨越多个磁盘

现在,如果您想查找或确定某个号码不存在,您只需询问相关人员您要查找的号码是否在他们持有的卡上。

如果在 N 秒内没有人回答,则该号码不存在。这是假设每个人都能听到您的声音,并且每个人都知道他们卡上的号码。

我对物理学知之甚少(声速、空气摩擦、每个人的大脑查看卡片的时间等)

【讨论】:

  • @NickJohnson - 是的,但这是努力的复杂性。通过并行运行任务,时间复杂度已降低到 O(1)。
【解决方案2】:

奇怪的是,问题是确定一个值的不存在,而不是存在。

这可能意味着它们引用了 Bloom 过滤器 (http://en.wikipedia.org/wiki/Bloom_filter)。布隆过滤器可以告诉你一个元素是否:

  • 不存在
  • 或可能存在

【讨论】:

  • 布隆过滤器是概率性的。此外,问题是“找到或确定不存在”,所以它不仅仅是不存在。即使它只是不存在,也可能存在误报(即它不存在,但我们说它存在)。
  • 布隆过滤器的构造不是 O(n) 吗?
  • @academicRobot:是的,但我认为我们可以这样做一次并摊销:)
  • 我认为如果这是作为答案给出的,那么应该说明它是一种概率算法,“仅”在 99% 的情况下有帮助(嗯,应该说明:)。还有一个强有力的论点,即这是一个实际的答案在于示例 (en.wikipedia.org/wiki/Bloom_filter#Example) - 似乎 Google 的 Big Table 使用了这个,所以如果他们或他们的竞争对手/同行(其他人?)会提出,我至少不会感到惊讶这在面试问题中(所以我基​​本上放弃了自己的答案:))
  • 我知道 Venti(来自贝尔实验室的 Plan 9 下的文件系统)解决了一个与布隆过滤器非常相似的问题,但是布隆过滤器的大小与 n 有关系,因此可能会超过原子操作.当它发生时,它成为 n 的函数来搜索它,而那是在我们考虑误报之前。构建布隆过滤器也是 n 的函数,通常至少是线性的。散列函数也不是免费的。简而言之,我会说这当然不是恒定的时间,即使在误报的可能性之前。布隆过滤器基本上是我能做的最好的了。
【解决方案3】:

如果只使用比较,我们有一个 Omega(log N)(最坏情况)下限(即 O(1) 是不可能的)。

假设您决定查看数组中的某个位置,那么您的对手可以将该元素放置在数组中较大的部分中。

因此,在每个步骤中,您至少要考虑一半的元素,因此在最坏的情况下是 Omega(logn)。

因此,您可能需要避免仅使用比较,以便在最坏的情况下比 O(log N) 做得更好。

正如提到的其他答案,您可以进行概率常数时间搜索,以合理的概率为您提供正确答案,例如使用布隆过滤器。

【讨论】:

    【解决方案4】:

    按照问题的字母,他们可能正在寻找插值搜索,这是平均情况 O(log log n)。是的,在最坏的情况下这是 O(n),但可以通过了解分布或使用插值二进制搜索来改进。

    这与 M >> N 提示有关。插值搜索的平均案例分析非常复杂,因此我什至不会尝试在 M >> N 下进行修改。但是在概念上,在 M >> N 下并假设均匀分布,您可以假设该值将由初始搜索位置的 +/-1,产生 O(1)。

    一个实际的实现可以进行一次初始插值,如果搜索值不受限制,则回退到二分搜索。

    虽然不确定如何在这种方法中使用多个磁盘来发挥优势...

    【讨论】:

    • 也许初始插值的一个步骤可以让您以正确的边界到达磁盘,然后您将阵列的很大一部分砍掉?
    • +1,一些注意事项:如果分布不均匀,如果您知道分布,仍然可以使用插值/外插搜索(对于均匀分布,您有线性插值/外插,对于其他分布则不是线性但取决于分布)。构建完美的分布表示可能需要大量空间,因此必须使用一些近似值/精度损失——目前尚不清楚这是否会消除潜在的好处。
    【解决方案5】:

    第一眼

    M >> N 我认为不是一个提示,它只是不鼓励创建一个位图,如果一个数字存在,它会在 O(1) 时间内直接告诉你。

    我认为 N 跨越多个硬盘的合理假设是,您可以预期您不会有更多磁盘可供您使用。因为您需要 2M 空间来获得 O(1) 性能,如果 N 跨越多个磁盘,则 M 跨越 >> 多个磁盘和 2M 跨越 >> 磁盘而不是可用的.

    此外,它告诉您存储 缺失的 数字的方法效率不高,因为那时您必须将 X 数字存储在

    X = M - N => X ~ M (因为 M >> N)

    那就更糟了。

    所以乍一看似乎你可以证明没有已知的更好的答案。

    编辑: 我仍然坚持上述推理,Moron 的回答也更好地证明了这一点。然而结论是,在从 Patrick 的回答中查看 Bloom Filter 之后,我相信面试官可能一直在研究这个和其他概率算法(应该在面试问题中指出)。

    【讨论】:

      【解决方案6】:

      如果我们所能做的只是比较,那么正如上面的海报所指出的,我们不能做得比 O(log(N)) 更好。

      但是,如果我们对输入分布有更多了解,我们就可以做更多的事情。如果被告知(由面试官:))数字是连续的,那么 O(1) 解决方案是可能的。第一个元素和我们正在寻找的元素之间的差异将为我们提供一个我们应该期望找到数字的确切位置。

      【讨论】:

      • 感谢您澄清“如果我们所能做的只是比较,那么正如上面的海报所指出的,我们不能做得比 O(log(N)) 更好”
      【解决方案7】:

      由于我们知道数字 (M) 的范围,我们可以执行插值二分搜索。而不是将搜索范围平分 1/2 将其平分 N / (HI - LO)。结果仍然是 O(log N),但常数较低。如果我们知道数据中没有重复项,这种技术会更好地工作,并且问题似乎暗示可能是这种情况,但这不是确定的。

      例如看这个博客:Faster than Binary Search

      【讨论】:

        【解决方案8】:

        嗯,据我所知。在这个问题中,您可以利用两个提示。 1. 数字已排序 2. N & M 非常大(N >> M)且 M 跨越多个磁盘

        您可以在这个问题中使用一些随机化。不是使用二分搜索,而是随机选择一个点,然后检查 x(要搜索的数字)是否小于或大于当前值。您可以从两端开始,并迭代地减小搜索空间的大小。只有在非常小的迭代中,您才能将其缩小到小域,然后您可以应用二进制搜索来提高效率。

        【讨论】:

          【解决方案9】:

          可以证明的事实是,任何进行比较的算法都不可能击败 log(n)。这意味着恒定时间解决方案无法将数字相互比较。在所有情况下,恒定时间的解决方案都会涉及诡计。

          鉴于此,通过一系列假设可以得到恒定时间解决方案:

          • 数字是按顺序写的
          • 您确切地知道数字序列的开始位置和结束位置(磁盘偏移)
          • 所有磁盘的大小都相同,并且具有精确相同的位容量
          • 您确切知道可以将多少位写入磁盘

          鉴于这些假设,只需将数字的位数乘以 k 即可。寻找那个位置 (O(1)) + offset 并读回正确的位数。

          【讨论】:

            【解决方案10】:

            问题是关于不存在的,所以不需要在磁盘中搜索。 我们可以检查数字 X 是否超出 O(1) 中所有磁盘的最小和最大范围。 (磁盘数量不变)

            bool not_exists=true
            for each disk_i in disks:
              not_exists &&= (X <min_element(disk_i)  || X > max_element(disk_i) )
            return not_exists
            

            如果结果为真。那么我们可以确定磁盘中没有X。 否则 X“可能”在磁盘中。

            【讨论】:

            • 这很聪明!
            【解决方案11】:

            如果您允许自己使用一些元数据,我认为您可以获得更快的查找时间。

            设置多个间接块或列表,其元素指向更多间接块/列表。重复直到达到所需的直接块/列表级别。这个想法是使用类似于某些文件系统访问其文件数据的方式(直接、间接、双重间接和三重间接块)。对于他们要求的数字范围,您很可能需要不止三重间接。

            您要查找的数字的每个部分都可以引用间接/直接表中的单独索引。最终,您将搜索分解得足够远,以至于您可以阅读可能包含或不包含数字的最后部分。然后,您可以使用您选择的算法搜索这最后一部分。

            希望这会有所帮助且有意义。

            免责声明:我马上就要吃午饭了,所以我还没有完全考虑清楚——这可能不切实际。

            【讨论】:

            • 这仍然是 O(log n) - 问题寻找算法来击败它。
            【解决方案12】:

            这很可能是一个措辞不当的问题。

            如果 Bloom 过滤器是他们正在寻找的答案(这很可能),则无需将候选者与潜在的分布式/并行算法元素(多个磁盘)混淆。

            假设一个磁盘

            一旦构建过滤器,布隆过滤器就是恒定时间操作。但是,为了弥补误报,必须进行二分搜索(甚至像有人建议的假设均匀分布的插值搜索)在二分搜索情况下会导致大于常数 log(n) 的因子。

            原来如此 O(k) + 1% * log(n) 。 O(k) 恒定时间检查布隆过滤器。 然后假设布隆过滤器的错误率(误报)为 1%,那么很多时候人们将不得不进行二分搜索以确保它确实存在。

            我不确定这可以通过摊销分析减少到恒定时间(不太精通那里)。

            【讨论】:

              【解决方案13】:

              尚未提及的一个方面是,该问题并未具体说明您使用的是哪种计算机。如果每个硬盘恰好连接到自己的 CPU,那么在恒定时间内执行此操作是微不足道的。

              这似乎是一种逃避,但如果这个问题是由从事分布式计算的面试官提出的,那么这可能就是他们正在寻找的答案。

              【讨论】:

                【解决方案14】:

                只是一个谦虚的想法。

                也许这更像是一个系统问题而不是算法问题,让我们尝试从搜索引擎的方式思考。

                假设我有足够的机器来索引所有排序的 N 个整数,每台机器只保存固定数量的 K 个文档,代表 N 个整数中的 K 个。

                所以这样对于任意给定的数字X,客户端查询服务器到达搜索节点的联网时间可以视为常数时间;搜索节点查找代表数字 X 的文档的时间也是常数时间,因为每个搜索节点上的文档数量是固定的数字 K。

                因此总时间是恒定的。不过,这或多或少与恩里克所说的相似。

                【讨论】:

                  【解决方案15】:

                  您可以通过检查包含数字的文件的大小然后创建一个大小大于文件大小的数字来解决这个问题(不是说 abt int 或 lar

                  【讨论】:

                    【解决方案16】:

                    我认为问题清楚地表明您给了一个大小为N 的列表,例如

                    const int N = 15;
                    int xs[N] = {1, 3, 7, 9, 13, 16, 17, 19, 21, 24, 25, 26, 27, 28, 30};
                    

                    您必须回答单个查询(少于O(logN)),因此您无法真正进行任何预处理。我相信在这种情况下,如果您可以分期付款,问题的措辞会有所不同。

                    N 实际上可能非常大,所以即使是数字N 本身也可能需要存储许多磁盘(我阅读问题的方式:)。我认为这只是表示您无法创建大小为 M 的简单查找数组,因为M &gt; N,因此没有意义。

                    所以,你真的不能比二分搜索做得更好。但是,由于您知道元素的最大可能值,即M(并假设数据是均匀分布的),您可以猜测初始位置,即开始二分查找的位置。

                    这本质上是x / M * N,代码可能是这样的:

                    double hint = static_cast<double>(x) / M; // between [0,1)
                    int m = static_cast<int>(hint * N); // guess the position in xs
                    // do binary search using m as initial "middle" point.
                    

                    因此,在假设成立的情况下,这种猜测将通过一个很好的常数来加速算法。不过,时间复杂度仍为O(lgN)

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-05-24
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-11-12
                      相关资源
                      最近更新 更多