【问题标题】:Most Efficient Way to Find Whether a Large List Contains a Specific String (Python)查找大列表是否包含特定字符串的最有效方法(Python)
【发布时间】:2010-02-19 17:53:45
【问题描述】:

我有一个包含大致所有英文单词的文件(~60k 词,~500k 个字符)。我想测试我收到的某个单词是否是“英语”(即,这个确切的单词是否在列表中)。

在 Python 中最有效的方法是什么?

简单的解决方案是将文件加载到列表中并检查单词是否在该列表中。列表可以排序,我相信这会将复杂度降低到 O(logn)。但是,我不确定 Python 如何实现通过列表进行搜索,以及如果内存中存在如此大的列表是否会降低性能。我可以“滥用”我可以限制单词长度的事实吗? (例如,最长的是 15 个字符)。

请注意,我在具有大量内存的机器上运行应用程序,因此我关心的不是内存消耗而是速度和 CPU 利用率。

谢谢

【问题讨论】:

    标签: python string


    【解决方案1】:

    python Set 是你应该尝试的。

    集合对象是不同的可散列对象的无序集合。常见用途包括成员资格测试、从序列中删除重复项以及计算数学运算,例如交集、并集、差分和对称差分。

    【讨论】:

    • 您认为 set 和 freezeset 之间有什么速度差异吗?
    • 请注意,切换到set 的改进可能很大。就我而言,检查 1000 次元素是否属于 270.000 个没有重复元素的列表大约需要 20-25 秒。检查它是否属于一个集合只需要大约 0.005 秒。
    【解决方案2】:

    Trie 结构将适合您的目的。毫无疑问,那里有 Python 实现......

    【讨论】:

      【解决方案3】:

      示例 Python 代码:

      L = ['foo', 'bar', 'baz'] # Your list
      s = set(L)  # Converted to Set
      
      print 'foo'  in s # True
      print 'blah' in s # False
      

      【讨论】:

      • 如果您只进行几次查找,从 list->set 转换所花费的时间可能比使用 set 节省的时间要长。取决于列表的大小和数量当然是循环
      【解决方案4】:

      你基本上是在测试一个成员是否在一个集合中,对吧?

      如果是这样,并且因为你说你有很多内存,为什么不将所有单词作为键加载到 memcache 中,然后为每个单词检查它是否存在于 memcache 中。

      或者使用 bash 使用的数据结构来自动完成命令名称 - 这在内存中快速且高效(不记得名称)。

      【讨论】:

        【解决方案5】:

        500k 个字符不是一个大列表。如果您的列表中的项目是唯一的,并且您需要重复进行此搜索,请使用 set,这将在最好的情况下将复杂性降低到 O(1)

        【讨论】:

        • 完全正确 - 集合是使用 Hashtables 构建的 - 因此 O(1)
        【解决方案6】:

        两件事:

        Python 'mutable set' 类型有一个 'add' 方法( s.add(item) ),因此您可以直接从大文件中读取(一行)到一个集合中,而无需使用列表作为中间数据结构。

        Python 允许您“腌制”数据结构,因此您可以将大集合保存到文件中并节省重新启动集合的时间。

        其次,我一直在寻找英语中所有单音节单词的列表以供自己消遣,但我发现提到的那些似乎是专有的。如果不打扰,请问你的英文单词表是否可以被别人获取?

        【讨论】:

        • 你甚至不需要 .add()。 set 将迭代器作为参数,因此假设每行存储一个单词,"f=open("words.txt") ; s = set(f)" 将起作用,并且不使用不必要的列表。虽然酸洗不是一个好主意 - 从泡菜中恢复可能至少需要与重建集合一样长的时间。如果初始化时间很重要,那么使用像 dbm 库这样的磁盘格式会更好。
        【解决方案7】:

        其他人使用 set() 为您提供了内存中的方式,这通常是最快的方式,并且不应该为 60k 字的数据集(最多几个 MiB)占用您的内存。您应该能够使用以下方法构建您的集合:

        f=open('words.txt')
        s = set(word.strip() for word in f)
        

        但是,将集合加载到内存中确实需要一些时间。如果您要检查很多单词,这没问题 - 查找时间将远远弥补它。但是,如果您每次执行命令时只检查一个单词(例如,这是一个类似 "checkenglish [word]" 的命令行应用程序),则启动时间将比您仅搜索文件行所花费的时间长按行。

        如果这是您的情况,或者您有更大的数据集,则使用磁盘格式可能会更好。最简单的方法是使用dbm 模块。从单词列表创建这样一个数据库:

        import dbm
        f=open('wordlist.txt')
        db = dbm.open('words.db','c')
        for word in f:
            db[word] = '1'
        f.close()
        db.close()
        

        然后您的程序可以通过以下方式检查成员资格:

        db = dbm.open('words.db','r')
        if db.has_key(word):
            print "%s is english" % word
        else:
            print "%s is not english" % word
        

        这将比设置查找慢,因为会有磁盘访问,但会比搜索快,内存使用率低且初始化时间不长。

        还有其他选择,例如使用 SQL 数据库(例如 sqlite)。

        【讨论】:

        • 请记住,直接从文件构造集合虽然很优雅,但会包含行尾字符,这可能不是您想要的。
        • 哎呀,你是对的。更新为去除行尾/额外的空格。
        【解决方案8】:

        如果内存消耗不是问题并且单词不会改变,那么最快的方法是将所有内容放在哈希中并以这种方式搜索。在 Python 中,这是Set。您将进行恒定时间查找。

        【讨论】:

        • +1,但我会带出旧的锯子:在哈希表中查找并不是真正的 O(1)——如果 (a) 数据集足够小并且(b) 您不存储产生 O(n)(类似链表)查找时间的一组病态键。在实践中(b)几乎从不违反,但是许多实现违反了(a),通过根据存储在哈希表中的元素数量调整桶的数量。但不管真正的时间复杂度如何,哈希表在你的情况下应该可以很好地工作。
        • Python 在其实现过程中广泛使用哈希表(所有类成员、模块等)。几乎所有内容都存储在 python 的哈希表中,因此,您会发现 python 哈希表实现是最好和最有效的实现之一,至少在“日常使用”方面是这样
        • 我的印象是集合是用平衡树实现的,而不是散列(这意味着 O(log n) 查找)。这对不对?
        【解决方案9】:

        仅当您对数据重复运行这种查询时,将列表转换为集合才会有帮助,对列表进行排序和进行二分搜索也是如此。如果您只打算从列表中提取数据一次,那么最好的选择是简单的旧线性搜索:

        if 'foo' in some_list:
            do_something()
        

        否则,最好的办法是使用前面提到的集合或二分查找。您应该选择哪一个在很大程度上取决于数据有多大以及您可以腾出多少内存。我听说非常大的列表往往会从散列中受益更多,尽管占用的内存量可能非常昂贵。

        最后,第三种选择是您可以将数据导入 sqlite 数据库并直接从中读取。 Sqlite 非常快,它可以省去从文件中加载整个列表的麻烦。 Python有一个非常好的内置sqlite library

        【讨论】:

          猜你喜欢
          • 2020-01-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-17
          • 2012-12-27
          • 2015-01-31
          相关资源
          最近更新 更多