【问题标题】:Design an algorithm, find the most frequently used word in a book设计一个算法,找出一本书中最常用的词
【发布时间】:2012-02-04 10:04:54
【问题描述】:

面试题:

找出书中最常用的词。

我的想法:

使用哈希表,遍历并标记哈希表。

如果知道书的大小,如果发现任何单词被使用> 50%,那么在接下来的遍历中跳过任何新单词,只计算旧单词。如果书的大小不知道怎么办?

是O(n)和O(n)的时间和空间。

有更好的想法吗?

谢谢

【问题讨论】:

  • 更改了标签,如果不合适请告诉我。似乎不是特定语言的问题。
  • 散列是很好的启发式方法,但它没有得到准确的答案(实际上两个字符串可能散列到相同的整数)另外,如果你想找到最频繁的单词,我认为你应该跳过单词喜欢the, then,...,因为它们很可能会出现频率最高,但这对每个人来说都不是好消息,因为这本书有the作为最频繁出现的词。
  • user1002288,你在这个线程上得到了很多不好的建议。几乎所有答案都来自实际/实施的角度,这可能不是面试官想要的。您可能想从理论的角度来看待这个问题。如果你在cstheory.stackexchange.com 上问这个问题,你可能会得到更好的答案。

标签: python algorithm data-structures hash


【解决方案1】:

要确定复杂性,我认为您需要考虑两个变量,n = 总词数,m = 唯一词数。我想最好的情况复杂度将接近 O(n log(m)) 的速度和 O(m) 的存储,假设每次迭代 n 个单词中的每一个,并基于哈希表构建和搜索或其他最终包含 m 个元素的此类结构。

【讨论】:

    【解决方案2】:

    这其实是map reduce的经典例子。

    维基百科页面中的示例将为您提供每个唯一单词的字数,但您可以轻松地在 reduce 步骤中添加一个步骤来跟踪当前最常见的单词(使用某种互斥锁来处理并发问题)。

    如果您有一个分布式机器集群或高度并行化的计算机,这将比使用哈希表运行得快得多。

    【讨论】:

      【解决方案3】:

      通常Heap 是当我们必须确定最常用/最少使用的数据时非常适合的数据结构。

      甚至用于这些目的的Python;s Counter.nlargest 也是通过堆数据结构实现的。

      二进制堆数据结构具有以下复杂性

      CreateHeap - O(1)
      FindMin - O(1)
      deleteMin - O(logn)
      Insert - O(logn)
      

      我对 Hash(在 Python 中使用默认字典)和 Heap(在 Python 中使用 Collections.Counter.nlargest)进行了比较,Hash 的表现略好于 Heap。

      >>> stmt1="""
      import collections, random
      somedata=[random.randint(1,1000) for i in xrange(1,10000)]
      somehash=collections.defaultdict(int)
      for d in somedata:
          somehash[d]+=1
      maxkey=0
      for k,v in somehash.items():
          if somehash[maxkey] > v:
              maxkey=k
      """
      >>> stmt2="""
      import collections,random
      somedata=[random.randint(1,1000) for i in xrange(1,10000)]
      collections.Counter(somedata).most_common(1)
      """
      >>> t1=timeit.Timer(stmt=stmt1)
      >>> t2=timeit.Timer(stmt=stmt2)
      >>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=10)/10)
      38168.96 usec/pass
      >>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=10)/10)
      33600.80 usec/pass
      

      【讨论】:

      • 为了让答案更完整,您介意说明基于堆的解决方案的时间和空间复杂度吗?谢谢。
      • @Aix,实际上维基链接有信息。无论如何我都会在这里添加它,这将更有意义
      • stmt1可以优化:max(((v, k) for k,v in somehash.iteritems()))
      • @Abhijit, Counter 不是基于Heap source,它只使用heapq.nlargest 函数来查找最常见的元素。关于时间比较,所有时间都足够接近。我收到dd: 22686.73; cnt: 22744.79; ddopt: 20243.07
      • 您似乎认为 Counter 将是最快的解决方案,然后您发布了一个基准,显示 Hash(默认 python dict)是最快的解决方案?你不是在打败自己的论点吗?
      【解决方案4】:

      您的优化有一个概括——如果已知书本大小并且您看到的任何单词的计数 > 剩余单词数 + 次高计数,则您当前计数最高的单词就是答案。

      【讨论】:

        【解决方案5】:

        您的解决方案是正确的、快速的,并且从实际的角度来看可能是最好/最简单的。

        其他发帖人的解决方案比您的解决方案具有更糟糕的时间复杂度。对于您正在使用的哈希,时间复杂度确实是 O(n)。每次插入是 O(1) 并且有 n 个单词,因此插入阶段花费 O(n)。迭代并找到最大值是O(n)。正如你提到的,空间也是 O(n)。

        请注意,您将无法使用 Chris 的解决方案提前终止您的算法,因为搜索您的哈希表的成本很高,而且您无法在每次插入后在 O(1) 时间内执行此操作。

        堆会花费更多时间,因为您需要在每次插入期间维护堆。堆插入是 O(log(n)),因此插入的总成本是 O(nlog(n))。

        【讨论】:

        • 有人认为您可能已经忽略了。生成哈希键的复杂性。
        • 你是说生成一个哈希键需要超过 O(n) 时间吗?请解释。为每次插入应用哈希键需要 O(1)。
        【解决方案6】:

        如果您正在处理一本书,那么您就知道词汇和大概的词频。即使您事先没有得到这些信息,您也可以通过扫描随机样本得到一个很好的估计。

        对于确切的答案,我会使用 k 个最常见单词的完美哈希函数。一个完美的散列函数需要 O(k) 内存并保证快速的最坏情况 O(1) 查找。

        对于不常见的词,我会使用优先队列实现为堆或自平衡树。常规哈希表也可能是一个不错的选择。

        【讨论】:

          猜你喜欢
          • 2013-07-06
          • 1970-01-01
          • 1970-01-01
          • 2021-06-11
          • 2014-10-16
          • 1970-01-01
          • 2018-09-10
          • 2014-08-11
          • 1970-01-01
          相关资源
          最近更新 更多