如何通过字符串中的大量字符优化迭代?想象一下情况:
a = []
b = []
for char in characters: # characters are string of 300 different characters (integers and letters)
if char.isalpha() and char.islower():
a.append(char)
else:
b.append(char)
如果characters
中有300个字符(整数和字母),如何加快执行速度?
我尝试使用a = ''
和a += char
,但事实证明,在计时时使用list更快。
这是我想出的代码。可以,但是太慢了:
def scramble(s1, s2):
for char in s2:
if char not in s1:
return False
s2_chars, s1_chars = [], []
duplicate_chars_s2 = []
duplicate_chars_s1 = []
found_all_duplicates = True
for char in s2:
if char in s2_chars:
duplicate_chars_s2.append(char)
s2_chars.append(char)
for char in s1:
if char in s1_chars:
duplicate_chars_s1.append(char)
s1_chars.append(char)
for char in duplicate_chars_s2:
if char not in duplicate_chars_s1:
found_all_duplicates = False
return True if found_all_duplicates else False
现在,当s1
和s2
是300个长字符串时,尝试运行该函数。
该函数的目的是确定s1
中的字符是否可以在s2
中构建字符串。含义...我需要一个计算重复字符并确定s1
中是否有足够的重复来构建所需字符串的代码。
答案 0 :(得分:2)
in
操作可能会很昂贵。如果您有很多set
操作,则应使用list
而不是in
。您可以在the python docs中阅读有关set
的更多信息。简而言之,与set
中的O(n)相比,list
中的查找时间为O(1)。
Python docs也说点很贵,因为每次迭代都必须重新评估。您可以使用以下方法摆脱它们:
a = []
a_append = a.append
a_append('string')
这将使添加到列表的速度更快。然后,您可以将其转换为集合并执行in
操作,然后就可以开始使用了。
答案 1 :(得分:2)
另一个更快的解决方案,在11.x秒内被接受。 @chepner显示的常规Counter
解决方案将在不到1秒的时间内被接受。
def scramble(s1, s2):
it = iter(sorted(s1))
return all(c in it for c in sorted(s2))
这是问题所在,顺便说一句:https://www.codewars.com/kata/55c04b4cc56a697bb0000048/train/python
答案 2 :(得分:1)
假设显示的代码是瓶颈(我对此表示怀疑,但让我们继续),您可以做的最昂贵的事情就是重复属性查找。
a = []
b = []
# Cache the bound methods
add_to_a = a.append
add_to_b = b.append
# Cache the unbound methods
is_alpha = str.isalpha
is_lower = str.islower
for char in characters:
if is_alpha(char) and is_lower(char):
add_to_a(char)
else:
add_to_b(char)
在涉及string.ascii_lowercase
,string.ascii_uppercase
,string.digits
和string.punctuation
的300个随机选择的字符的测试中,上面的过程耗时35微秒,而原始时间为54微秒代码。
不过,我会这样写您的scramble
:
from collections import Counter
def scramble(s1, s2):
c1 = Counter(s1)
c2 = Counter(s2)
return all(c2[c] <= c1[c] for c in s2)
只要s2
中的每个字符出现在s1
中的次数至少与{{1}中出现的次数相同,您就可以根据s2
中的字符来构建c1
}}。 c2
用C语言实现,应该能够比任何等效的Python代码更快地计算字符数。
答案 3 :(得分:1)
基准在我的PC上的三种解决方案(以及一些恕我直言有趣的分析):
scramble1 0.235 seconds
scramble2 0.232 seconds
scramble3 0.054 seconds
Their“性能测试”是十个测试,仅描述为“ “测试两个字符串,最大不超过600000个字符” ,问题文本显示为“只有小写字母会被使用(az)”。。因此,在我的基准测试中,我从a-z字符串中提取了s1
个60万个随机字母,并将s2
对其进行了随机排列。那样难。
现在...所有三种解决方案在此处提交后,将在10到12秒左右被接受(由于运行时间的不同,并非总是如此)。为什么那里的第三个解决方案没有更快?我怀疑如此处的讨论所述,Python 2解决方案不起作用,因为法官崩溃了:
Traceback (most recent call last):
File "main.py", line 51, in <module>
do_test()
File "main.py", line 17, in do_test
from random import randint, choices, shuffle
ImportError: cannot import name 'choices'
所以我想他们的法官使用choices
和shuffle
类似于我的做法。让我们测量一下:
t0 = time.perf_counter()
s1 = ''.join(random.choices(string.ascii_lowercase, k=600_000))
a2 = list(s1)
random.shuffle(a2)
s2 = ''.join(a2)
t1 = time.perf_counter()
print(t1 - t0)
大约花费0.65秒。远远超过解决方案所需的0.05到0.24秒!因此,我怀疑总时间包括法官生成输入的时间,而这是总时间的大部分。
基准代码:
import string
import random
from timeit import timeit
from collections import Counter
def scramble1(s1, s2):
c1 = Counter(s1)
c2 = Counter(s2)
return all(c2[c] <= c1[c] for c in s2)
def scramble2(s1, s2):
it = iter(sorted(s1))
return all(c in it for c in sorted(s2))
def scramble3(s1, s2):
return all(s1.count(c) >= s2.count(c) for c in set(s2))
# Generate hardest test case
s1 = ''.join(random.choices(string.ascii_lowercase, k=600_000))
a2 = list(s1)
random.shuffle(a2)
s2 = ''.join(a2)
# Run the benchmarks
for _ in range(3):
for scramble in scramble1, scramble2, scramble3:
seconds = timeit(lambda: scramble(s1, s2), number=1)
print(scramble.__name__, '%.3f' % seconds, 'seconds')
print()
另一件事:迄今为止最快的解决方案是将每个字符串遍历26次的解决方案。当26次更多工作时,怎么能快4.6倍 ?好吧,这是因为在字符串中搜索单个字符的速度异常快。我测量了一下(虽然我认为是index
,而不是count
),但发现它每秒检查50亿个字符。是的, b 一百万!更令人震惊的是它是在4 GHz CPU上!每个CPU周期如何检查多个字符?!我检查了源代码,如果我没记错的话,它在内部使用C的memchr
,而C依次使用一些多字节CPU指令来同时检查多个字节。