我正在对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()慢于此?谢谢!
答案 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最快的原因是
issubset(object)
实际上隐含地将object
转换为set
。这很昂贵。
这种比较是不公平的。假设我们在方法一中有一个单词"qsdfwe"
,它将检查是否在r1
中,并且在下面的循环中,如果找不到r1
的子集,则有也不可能是其他人的子集。
所以我实现了一个更快的方法,我认为我们可以专注于由第一个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)