我想从我的代码加速这个功能,这个代码经常被调用。该函数接收字符串的输入列表(通常长度为4)并产生字符串列表,其中对应的条目被替换为大写字母,其顺序对应于输入字符串的字母数字顺序。然后将此列表合并为一个字符串。
示例:输入列表['wwTv', 'NzkT', 'wwTv', 'JhXc']
,输出字符串'C,B,C,A'
。在实际示例中,每个列表中都有许多重复项。
您能否提出更有效的解决方案?或者我直截了当的算法足够好并且不能得到显着改善?
以下是我的代码示例(Python 3.2)。这里输入数据的样本是随机创建的,并传递给函数f
。
import timeit
import string, random
dumb_label_set = ['A', 'B', 'C', 'D', 'E']
def a(labels):
uniq_labels = sorted(set(labels))
dumb_labels = [dumb_label_set[uniq_labels.index(a)] for a in labels]
s_name = ','.join(dumb_labels)
return(s_name)
def b(labels):
uniq_labels = {l: i for i, l in enumerate(sorted(set(labels)))}
dumb_labels = [dumb_label_set[uniq_labels[a]] for a in labels]
s_name = ','.join(dumb_labels)
return(s_name)
labels = []
for i1 in range(100000):
labels.append([''.join(random.choice(string.ascii_letters) for ii in range(random.randint(1,4))) for i2 in range(4)])
start = timeit.default_timer()
res_a = [a(l) for l in labels]
print(timeit.default_timer() - start)
start = timeit.default_timer()
res_b = [b(l) for l in labels]
print(timeit.default_timer() - start)
print(res_a == res_b)
结果:
0.41835449560994675
0.4420497451417873
True
我的功能a
稍快一些,然后由Martijn Pieters提出b
答案 0 :(得分:3)
我使用字典将标签映射到索引:
uniq_labels = {l: i for i, l in enumerate(sorted(set(labels)))}
dumb_labels = [dumb_label_set[uniq_labels[a]] for a in labels]
使用较小的labels
集合以便在更加可管理的时间内进行多次传递,这样做:
>>> import timeit
>>> import string, random
>>> dumb_label_set = ['A', 'B', 'C', 'D', 'E']
>>> def f(labels):
... uniq_labels = sorted(set(labels))
... dumb_labels = [dumb_label_set[uniq_labels.index(a)] for a in labels]
... s_name = ','.join(dumb_labels)
... return(s_name)
...
>>> def f_dict(labels):
... uniq_labels = {l: i for i, l in enumerate(sorted(set(labels)))}
... dumb_labels = [dumb_label_set[uniq_labels[a]] for a in labels]
... s_name = ','.join(dumb_labels)
... return s_name
...
>>> labels = []
>>> for i1 in range(100):
... labels.append([''.join(random.choice(string.ascii_letters) for ii in range(random.randint(1,4))) for i2 in range(4)])
...
>>> timeit.timeit('[f(l) for l in labels]', 'from __main__ import f, labels', number=10000)
6.586822032928467
>>> timeit.timeit('[f(l) for l in labels]', 'from __main__ import f_dict as f, labels', number=10000)
7.633307933807373
但正如您所看到的,对于您的小型输入集,您的方法更快。看起来设置映射所需的时间比最多4 .index()
次查找要多。
如果您的标签序列包含(更多)元素,我的方法将获胜:
>>> dumb_label_set = string.ascii_uppercase
>>> labels = []
>>> for i1 in range(100):
... labels.append([''.join(random.choice(string.ascii_letters) for ii in range(random.randint(1,4))) for i2 in range(26)])
...
>>> timeit.timeit('[f(l) for l in labels]', 'from __main__ import f, labels', number=1000)
3.069930076599121
>>> timeit.timeit('[f(l) for l in labels]', 'from __main__ import f_dict as f, labels', number=1000)
2.404794931411743
这里最重要的一课是使用timeit
module来比较不同的方法。 timeit
模块为您的平台使用最佳计时器,并比较许多运行的被测代码以消除外部调度影响(磁盘I / O,其他进程等)。
即使只计时一次,使用timeit.default_timer
优于使用time.time
; 可能仍然是同一个计时器,但它将是您平台上最准确的时钟。
答案 1 :(得分:1)
如果你真的希望这个能够快速发挥作用,那么也要看看cython。当然,在这里看看其他提出的算法,但是一旦你选择了最快的算法,cython仍然可以给它一个很好的推动力。
目前正在使用原始方法a
和b
,我只是将它们移动而不更改为另一个模块,并使用cython和gcc(-O3)进行编译。没有类型信息,我得到以下时间:
a: 0.4449379859997862
b: 0.48829928699888114
a (cython): 0.29741462399942975
b (cython): 0.2461447869991389
我确信标记变量的类型可能会给它带来另一种提升。