【问题标题】:What is the fastest method for checking for duplicates in python?在 python 中检查重复项的最快方法是什么?
【发布时间】:2012-05-20 22:56:23
【问题描述】:

使用字典似乎很理想。

例如:

history = {}
for i in collection:
    if i not in history:
        history[i] = None
        # fancy computation here

使用 set() 类型会一样快吗? set() 不需要我向哈希键添加愚蠢的 None 值。

【问题讨论】:

    标签: python dictionary hashmap set duplicates


    【解决方案1】:

    dicts 更快,但只有一点点:

    import timeit
    
    setup = """
    x = range(10000)
    s = set(range(5000))
    d = dict.fromkeys(range(5000))
    """
    
    print '# set', timeit.timeit('for i in x: z = i in s', setup, number=1000)
    print '# dic', timeit.timeit('for i in x: z = i in d', setup, number=1000)
    
    # set 1.18897795677
    # dic 1.1489379406
    

    尽管如此,除非性能是绝对关键的,否则为了可读性,您应该使用集合。

    当然,正如您的问题所暗示的那样,我们谈论的是可散列类型。不可散列的类型(如容器)需要其他技术。

    为了完整起见,以下是不同修改方法的基准:

    import timeit
    
    setup = """
    x = range(10000)
    s = set(range(5000))
    d = dict.fromkeys(range(5000))
    
    add_method = s.add
    """
    
    print '# set-add     ', timeit.timeit('for i in x: s.add(i)', setup, number=1000)
    print '# set-closure ', timeit.timeit('for i in x: add_method(i)', setup, number=1000)
    print '# dict []     ', timeit.timeit('for i in x: d[i]=None', setup, number=1000)
    print '# d.setdefault', timeit.timeit('for i in x: d.setdefault(i)', setup, number=1000)
    
    # set-add      1.96829080582
    # set-closure  1.2261030674
    # dict []      0.982795000076
    # d.setdefault 2.27355480194
    

    dict[i] 是最快的,但这一次并不奇怪,因为不涉及函数调用。

    【讨论】:

    • 您的测试与问题不同。您不会增量地添加到集合/字典中。
    • @thg435,您是否运行了足够多的代码以使 dict 始终优于设置?计时算法不是检查速度的好方法。
    • @schlenk:“添加”代码对于这个问题并不重要,也不会影响时间。
    • @thg435 "add" 在调整哈希表大小和更改它时非常重要。在您的示例中,您有最佳情况来了解表格的最终大小并一步完成分配。
    【解决方案2】:

    是的,你应该使用一套。


    使用 set() 类型会一样快吗?

    不,它不会那么快。它会更快


    更新

    有些人发布的基准测试表明 set 比 dict 慢。我认为这有点令人惊讶,因为它们基本上具有相同的底层实现,只是集合更简单。我认为我找到了缓慢的原因:

    def set_way():
        my_set = set()
        my_set_add = my_set.add   # remember the method
        for ele in x:
            if ele not in my_set:
                my_set_add(ele)   # call the method directly
    

    结果:

    dict time : 1.896939858077399
    set time : 1.8587076107880456
    

    Set 现在比预期的要快一些。

    【讨论】:

    • 为什么更快?检查字典中的键需要恒定的时间,它是否与集合的精确算法相同?
    • @Ramin:是的,集合也使用哈希。集合中的项目必须是可散列的。
    • @Ramin set 的实现与dict 的实现几乎一模一样。
    • @MarkByers 如果列表包含一些unhashable 类型如lists,set 等怎么办?
    • 嗯,一个集合不需要为一个值分配内存,它只有一个键,所以它应该更快,使用更少的内存。
    【解决方案3】:

    字典似乎更快。

    import timeit
    import random as rn
    
    x  = [rn.choice(xrange(10000)) for i in xrange(1000)]
    
    def set_way():
        my_set = set()
        for ele in x:
            if ele in my_set:
                return True
            else:
                my_set.add(ele)
        else:
            return False
    
    def dict_way():
        dicto = {}
        for ele in x:
            if ele in dicto:
                return True
            else:
                dicto[ele] = None
        else:
            return False
    
    
    
    num = 10000
    
    set_time = timeit.timeit(set_way, number = num)
    print 'set time :', set_time
    dict_time = timeit.timeit(dict_way, number = num)
    print 'dict time :', dict_time
    

    结果:

    set time : 0.619757678699
    dict time : 0.466664548148
    

    【讨论】:

    • 设置比较慢?令人惊讶...你对此有什么解释吗?
    • 我也很惊讶。也许添加到 set 比添加到 dict 慢?我很想知道自己的解释是什么。
    • +1 用于发布令人惊讶的性能测量。有关说明,请参阅我的更新答案。
    猜你喜欢
    • 2017-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 1970-01-01
    • 2021-07-17
    • 1970-01-01
    相关资源
    最近更新 更多