通过字符串中的大量字符优化迭代

时间:2020-01-10 14:55:14

标签: python

如何通过字符串中的大量字符优化迭代?想象一下情况:

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

现在,当s1s2是300个长字符串时,尝试运行该函数。

该函数的目的是确定s1中的字符是否可以在s2中构建字符串。含义...我需要一个计算重复字符并确定s1中是否有足够的重复来构建所需字符串的代码。

4 个答案:

答案 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_lowercasestring.ascii_uppercasestring.digitsstring.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'

所以我想他们的法官使用choicesshuffle类似于我的做法。让我们测量一下:

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指令来同时检查多个字节。

相关问题