Python的issubset性能与set中的find元素相比较

时间:2018-04-11 07:18:37

标签: python python-3.x

我正在对Leetcode(500键盘行)进行编程练习,我在讨论中遇到了set.issubset()。与我自己的答案相比,这是一个有趣的发现:

1)

def checkWord(self, word):
    r1 = 'qwertyuiop'
    r2 = 'asdfghjkl'
    r3 = 'zxcvbnm'
    row = 0
    for idx, ch in enumerate(word):
        if idx == 0:
            row = 1 if ch in r1 else 2 if ch in r2 else 3
            continue
        coming_row = 1 if ch in r1 else 2 if ch in r2 else 3
        if row != coming_row:
            return False
    return True

如果我提交这个,我的运行时间为40毫秒

2)

def checkWord(self, word):
    r1 = 'qwertyuiop'
    r2 = 'asdfghjkl'
    r3 = 'zxcvbnm'
    return set(word).issubset(r1) or set(word).issubset(r2) or set(word).issubset(r3)

如果我使用issubset(),我的运行时间为36毫秒,超过所有提交的94.87%。

3)所以我想,嗯,与这件作品的不同之处是什么?根据我在这个主题The complextiy of Python issubset()上发现的一个问题,方法2)的时间复杂度应该是O(len(word)),我认为这应该与这篇文章相同:

def checkWord(self, word):
    r1 = set('qwertyuiop')
    r2 = set('asdfghjkl')
    r3 = set('zxcvbnm')
    row = 0
    for idx, ch in enumerate(word):
        if idx == 0:
            row = 1 if ch in r1 else 2 if ch in r2 else 3
            continue
        coming_row = 1 if ch in r1 else 2 if ch in r2 else 3
        if row != coming_row:
            return False
    return True

但是,运行时间为32毫秒,比所有py3提交量高100%...为什么issubset()慢于此?谢谢!

2 个答案:

答案 0 :(得分:1)

请注意,您的AsyncRestTemplate函数的第一个版本不会检查checkWord值是否设置为ch,因此您可以避免一次检查。

此外,在您的第二版r3中,您可以使用checkword参数的字符三次次创建一个集合。在使用'issubset'检查之前,为什么不存储该设置?您的代码将是这样的:

word

这是使用正则表达式的另一种解决方案:

def checkWord3(word):
    r1 = 'qwertyuiop'
    r2 = 'asdfghjkl'
    r3 = 'zxcvbnm'
    s1 = set(word)
    return s1.issubset(r1) or s1.issubset(r2) or s1.issubset(r3)

答案 1 :(得分:1)

我测试了以下方法:

import random
import string

def checkWord1(word):
    r1 = 'qwertyuiop'
    r2 = 'asdfghjkl'
    r3 = 'zxcvbnm'
    row = 0
    for idx, ch in enumerate(word):
        if idx == 0:
            row = 1 if ch in r1 else 2 if ch in r2 else 3
            continue
        coming_row = 1 if ch in r1 else 2 if ch in r2 else 3
        if row != coming_row:
            return False
    return True

def checkWord2(word):
    r1 = 'qwertyuiop'
    r2 = 'asdfghjkl'
    r3 = 'zxcvbnm'
    return set(word).issubset(r1) or set(word).issubset(r2) or set(word).issubset(r3)

def checkWord3(word):
    r1 = 'qwertyuiop'
    r2 = 'asdfghjkl'
    r3 = 'zxcvbnm'
    r = set(word)
    return r.issubset(r1) or r.issubset(r2) or r.issubset(r3)

def checkWord4(word):
    r1 = set('qwertyuiop')
    r2 = set('asdfghjkl')
    r3 = set('zxcvbnm')
    row = 0
    for idx, ch in enumerate(word):
        if idx == 0:
            row = 1 if ch in r1 else 2 if ch in r2 else 3
            continue
        coming_row = 1 if ch in r1 else 2 if ch in r2 else 3
        if row != coming_row:
            return False
    return True

并衡量执行时间:

word = ''.join(random.choice(string.ascii_lowercase) for _ in range(random.randint(0, 9)))

print("One:")
%timeit -n 10000 checkWord1(word)
print("Two:")
%timeit -n 10000 checkWord2(word)
print("Three:")
%timeit -n 10000 checkWord3(word)
print("Four:")
%timeit -n 10000 checkWord4(word)

获得结果:

One:
475 ns ± 88.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Two:
708 ns ± 117 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Three:
552 ns ± 19.6 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Four:
1.19 µs ± 10.8 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

虽然随机生成的字符串不同,但结果相似。

一个是最快的,三个比两个更快,更稳定,四个是最差的。

我认为为什么One最快的原因是

  1. issubset(object)实际上隐含地将object转换为set。这很昂贵。

  2. 这种比较是不公平的。假设我们在方法一中有一个单词"qsdfwe",它将检查是否在r1中,并且在下面的循环中,如果找不到r1的子集,则有也不可能是其他人的子集。

  3. 所以我实现了一个更快的方法,我认为我们可以专注于由第一个char决定的一行,因为这三行是独占的,所以我们不再检查其他行。

    def checkWord5(word):
        r = ['qwertyuiop', 'asdfghjkl', 'zxcvbnm']
        first_char = word[0]
        row = r[0] if first_char in r[0] else r[1] if first_char in r[1] else r[2]
        for ch in word[1:]:
            if ch not in row:
                return False
        return True
    

    获得结果:

    One:
    728 ns ± 236 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    Two:
    2.08 µs ± 55.8 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    Three:
    1.43 µs ± 60 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    Four:
    1.45 µs ± 7.74 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    Five:
    374 ns ± 7.01 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)