ASCII

时间:2016-04-28 08:32:41

标签: python

是否可以加速以下代码,但不使用外部模块(NumPy等)?只是简单的Python。两条思路:加速计算

chr(int(round( multiplOrds**(1.0/DLen), 0) ) )

或更快地建立所需结构。目的是找到ASCII符号的ord()的几何平均值,并将其报告为圆值(符号)。 len(InDict)是高于1的任何东西。例子的结果应该是

KM<I

代码:

def GA():
    InStr="0204507890"
    InDict={
       0:"ABCDEFGHIJ",
       1:"KLMNOPQRST",
       2:"WXYZ#&/()?"
       }

    OutStr = ""

    DLen = len(InDict)
    for pos in zip(InStr, *InDict.values()):
        if pos[0]=="0":
            multiplOrds = 1
            for mul in (ord(char) for char in pos[1:] if char!="!"): multiplOrds*=mul
            OutStr+= chr(int(round( multiplOrds**(1.0/DLen), 0) ) )

    return OutStr

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("GA()", setup="from __main__ import GA"))

6 个答案:

答案 0 :(得分:6)

首先想到的是:

连接字符串很慢,因为它们是不可变的,因此每次修改都会导致创建一个新的复制实例。这就是为什么你不应该做的事情:

s = ""
for i in range(1000000):
    s += chr(65)

每个循环它将创建一个新的字符串实例,其大小比前一个字符大一个,旧的实例将保留,直到垃圾收集器启动。同时分配内存很慢。

使用生成器表达式存储部分字符串并将它们最终连接在一起的速度大约是代码的两倍和更短:

s = "".join(chr(65) for i in range(1000000))

答案 1 :(得分:6)

我假设你使用的是python3。

以下是我可以通过提供的信息获得的最快的代码,GA_old是旧函数GA是优化函数。我将输入初始化移动到全局范围,因为优化初始化并不重要,我假设您从其他地方获取输入,并且不会一直使用相同的输入。

所以这是代码:

InStr="0204507890"
InDict={
   0:"ABCDEFGHIJ",
   1:"KLMNOPQRST",
   2:"WXYZ#&/()?"
}

def GA():
    OutStr = ""

    p = 1.0 / len(InDict)
    for pos,rest in zip(InStr, zip(*InDict.values())):
        if pos == "0":
            product = 1
            for char in rest:
                if char != '!':
                    product*= ord(char)
            OutStr += chr(int(round(product ** p)))
    return OutStr

def GA_old():

    OutStr = ""

    DLen = len(InDict)
    for pos in zip(InStr, *InDict.values()):
        if pos[0]=="0":
            multiplOrds = 1
            for mul in (ord(char) for char in pos[1:] if char!="!"): multiplOrds*=mul
            OutStr+= chr(int(round( multiplOrds**(1.0/DLen), 0) ) )
    return OutStr

if __name__ == '__main__':
    assert GA_old() == GA()
    import timeit
    print(timeit.timeit("GA()", setup="from __main__ import GA", number=100000))
    print(timeit.timeit("GA_old()", setup="from __main__ import GA_old", number=100000))

它在我的机器上生成以下输出(python 3.4.3):

 ⚡ python3 t.py 
0.6274565359999542
1.1968618339960813

我从下面得到的大部分加速:

  • 此处不使用列表推导for mul in (ord(char) for char in pos[1:] if char!="!"):。看起来像python创建2级迭代器,这是相当慢的。同时将这种理解嵌入到if的单循环中可以提高IMO的可读性。
  • 非常令人惊讶地)删除round函数的第二个参数非常显着地加速了函数。我无法解释这一点,它看起来像python中的小性能错误。

但我认为它可以进一步优化,但它需要了解您的真实数据(我猜你不会优化这个在几分之一秒内完成的特殊情况)。

一些想法:

  • 如果您的字符串很长,请尽量不附加给他们,但请使用其他答案的建议(bytearray"".join(...)
  • 尝试使用缓存chr(int(round( multiplOrds**(1.0/DLen))))(将已计算的结果放到全局字典中,如果可以在dict中找到它,则不重新计算值)。
  • 而不是计算multiplOrds**(1.0/DLen),您可以尝试为每个可能的字符预先计算ord(c) ** DLen的表格,并尝试在此预先计算的表格中搜索multiplOrds

这些想法都不会对您发布的输入数据有效,因为它非常小,但它可能对更大的输入有效。

答案 2 :(得分:3)

我不想做出疯狂猜测,因此您可以在分析器下运行代码,尤其是逐行分析器:https://zapier.com/engineering/profiling-python-boss/

此外,当您需要存储和连接ASCII字符列表时,最好使用bytearray代替liststring来存储OutStr

我注意到我的代码中的更改带有注释:

def GA():
    InStr="0204507890"
    InDict={
       0:"ABCDEFGHIJ",
       1:"KLMNOPQRST",
       2:"WXYZ#&/()?"
    }
    OutStr = bytearray() # use bytearray instead of string

    DLen = len(InDict)
    for pos in zip(InStr, *InDict.values()):
        if pos[0]=="0":
            multiplOrds = 1
            for mul in (ord(char) for char in pos[1:] if char!="!"):     multiplOrds*=mul
            OutStr+= chr(int(round( multiplOrds**(1.0/DLen), 0) ) )

    return str(OutStr) # convert bytearray to string

链接到有关bytearray的使用的非常有趣的话题:

http://rhodesmill.org/brandon/talks/(哦,来吧。谁需要Bytearrays?)

答案 3 :(得分:3)

您是否可以使用具有预先计算值的查找表或大开关语句?我不是蟒蛇人,但这看起来很乱。 :)

此外我还没有得到它实际做的事情,所以一些真实的例子(输入 - >输出)可以帮助人们更好地帮助你,甚至可以重新分配你的整个算法,而不仅仅是优化部件。

我试过python小提琴:InDict总是一样吗? InStr能比InDict长吗?为什么你只计算0值的东西?

基本上,您似乎将InDict的每一列都减少为这些数字,然后选择InStr为零的那一列:

var ia = [ 75, 76, 77, 78, 58, 60, 65, 62, 63, 73 ]

var sa = [ "K", "L", "M", "N", ":", "<", "A", ">", "?", "I" ]

这对我来说都没有意义,所以我现在出去,一旦你提供更多信息就会检查。另外,你编写代码的目的是什么?

小提琴的东西给了我KM,而不是KM&lt;我为你的榜样? &lt;很好,但是当我被追加时它似乎会破裂。

另外两个想法:

我认为x **(1 / n)是x的第n个根,如果n很小,可能会有更快的函数。

此外,既然您正在舍入值,您可以用泰勒和替换它,并在结果足够接近时立即停止计算。

您可以为所有值创建阈值,因此您只需浏览列表并找到正确的值。所以你只需要从0到maxX迭代一次并计算(x + 0.5)** n,应该更快。也许进行检查,包括低于/高于多少的值并调整阈值。

答案 4 :(得分:1)

这使用与dim-am相同的实现,但我似乎还没有能够发表评论。为了更加清晰地加速,我在时序代码中添加了一些内容。似乎发生了+/- 0.5%的漂移,但到目前为止,我找不到一种方法使其更快。

InStr="0204507890"
InDict={
   0:"ABCDEFGHIJ",
   1:"KLMNOPQRST",
   2:"WXYZ#&/()?"
}

def GA1():
    OutStr = ""

    p = 1.0 / len(InDict)
    for pos,rest in zip(InStr, zip(*InDict.values())):
        if pos == "0":
            product = 1
            for char in rest:
                if char != '!':
                    product*= ord(char)
            OutStr += chr(int(round(product ** p)))
    return OutStr

def GA():
    """Mess around with this one."""
    OutStr = ""

    p = 1.0 / len(InDict)
    for pos,rest in zip(InStr, zip(*InDict.values())):
        if pos == "0":
            product = 1
            for char in rest:
                if char != '!':
                    product*= ord(char)
            OutStr += chr(int(round(product ** p)))
    return OutStr


def GA_old():

    OutStr = ""

    DLen = len(InDict)
    for pos in zip(InStr, *InDict.values()):
        if pos[0]=="0":
            multiplOrds = 1
            for mul in (ord(char) for char in pos[1:] if char!="!"):     multiplOrds*=mul
            OutStr+= chr(int(round( multiplOrds**(1.0/DLen), 0) ) )
    return OutStr

if __name__ == '__main__':
    print("Ensuring equivalent functionality...")
    assert GA_old() == GA()
    assert GA_old() == GA1()
    print("OK.")
    print("Running timed test of alternative implementations, please wait...")
    import timeit
    tNew = (timeit.timeit("GA()", setup="from __main__ import GA", number=100000))
    tRef = (timeit.timeit("GA1()", setup="from __main__ import GA1", number=100000))
    tOld = (timeit.timeit("GA_old()", setup="from __main__ import GA_old", number=100000))
    percent_speedup = 100 * (1.0 - (tNew / tOld))
    percent_speedup_ref = 100 * (1.0 - (tRef / tOld))
    percent_relative = 100 * ( 1.0 - (tNew/tRef))
    print("Base time: {0}, New time: {1}, Speedup (%): {2}".format(tOld,tNew, percent_speedup) )
    print("Ref time: {0}, Ref Speedup (%): {1}, \nSpeedup relative to reference(%): {2}".format(tRef, percent_speedup_ref, percent_relative) )

答案 5 :(得分:-1)

虽然没有代码样本,但我还会权衡。

您正在尝试计算指数的乘积。而是计算对数。