【问题标题】:How to improve python dict performance?如何提高python dict性能?
【发布时间】:2016-09-13 20:07:49
【问题描述】:

我最近使用字典编写了一个 python 解决方案,它得到了 TLE 判决。该解决方案与 c++ 中的多集解决方案完全相似,它可以工作。所以,我们确信逻辑是正确的,但实现不达标。

理解下面代码的问题描述(http://codeforces.com/contest/714/problem/C):

  • 对于每个数字,我们需要得到一个由 0 和 1 组成的字符串,如果相应的第 i 个数字是偶数/奇数,那么第 i 个数字是 0/1。
  • 我们需要维护具有上述点给出的相同映射的数字计数。

任何提示/指针可以提高以下代码的性能?它为大型测试用例 (http://codeforces.com/contest/714/submission/20594344) 提供了 TLE (Time Limit Exceeded)。

from collections import defaultdict

def getPattern(s):
    return ''.join(list(s.zfill(19)))

def getSPattern(s):
    news = s.zfill(19)
    patlist = [ '0' if (int(news[i])%2 == 0) else '1'   for i in range(19) ]
    return "".join(patlist)


t = int(raw_input())
pat = defaultdict(str)  # holds strings as keys and int as value

for i in range(0, t):
    oper, num = raw_input().strip().split(' ')

    if oper == '+' :
        pattern = getSPattern(str(num))
        if pattern in pat:
            pat[pattern] += 1
        else:
            pat[pattern] = 1
    elif oper == '-' :
        pattern = getSPattern(str(num))
        pat[pattern] =  max( pat[pattern] - 1, 0)
    elif oper == '?' :
        print pat.get(getPattern(num) , 0 )

【问题讨论】:

  • 虽然不是性能调优方面的专家,但我希望字典查找性能相当高。我倾向于更多地研究getSPattern 函数,因为我相信可以从那里挤出一些东西。现在,在我们开始之前,我阅读了比赛,但无法得到时间限制的测量位置:它只是在'? ' 测试?
  • @sal 时间限制是根据每个测试用例执行来衡量的。因此,对于输入数字为 t 的大型测试用例,TLE 为 100000。如果您滚动到提交链接的最底部,您可以检查这一点。
  • 知道了。试试这个版本:eval.in/641639 我只更改了你的getSPattern,并去掉了defaultdict(尽管你可以保留它)。看看这是否会给您带来任何性能提升。如果是,那么我将添加一个包含更多详细信息的答案。
  • 我的机器一旦从这次故障中恢复过来,肯定会尝试一下 :) askubuntu.com/q/824585/56429
  • @sal 我没有正确使用 defaultdict。您的代码已被接受。 codeforces.com/contest/714/submission/20615167

标签: python python-2.7 performance dictionary


【解决方案1】:

我发现您的代码有很多小问题,但不能说它们是否会导致严重的性能问题:

您错误地设置和使用了您的defaultdict()

pat = defaultdict(str)
...
if pattern in pat:
    pat[pattern] += 1
else:
    pat[pattern] = 1

defaultdict() 构造函数的参数应该是值的类型,而不是键的类型。正确设置 defaultdict 后,您可以简单地执行以下操作:

pat = defaultdict(int)
...
pat[pattern] += 1

如果模式不存在,则该值现在默认为零。

因为规范说:

 -  ai — 从多重集中删除单次出现的非负整数 ai。保证至少有一个ai 多集。

然后这个:

pat[pattern] =  max( pat[pattern] - 1, 0)

可以是这样的:

pat[pattern] -= 1

您正在使用 19 个字符串,但由于规范说数字将小于 10 ** 18,因此您可以使用 18 个字符串。

getSPattern() 执行zfill() 然后处理字符串,它应该以相反的顺序执行,处理字符串然后zfill() 它,因为不需要在前导零上运行逻辑。

我们不需要int() 的开销来将字符转换为数字:

(int(news[i])%2 == 0)

考虑使用 ord() 代替,因为数字的 ASCII 值与数字本身具有相同的奇偶校验:ord('4') -> 52

而且你不需要遍历索引,你可以简单地遍历字符。

以下是我对您的代码进行了上述更改的返工,看看它是否仍然有效(!)并为您带来任何性能:

from collections import defaultdict

def getPattern(string):
    return string.zfill(18)

def getSPattern(string):
    # pattern_list = (('0', '1')[ord(character) % 2] for character in string)
    pattern_list = ('0' if ord(character) % 2 == 0 else '1' for character in string)
    return ("".join(pattern_list)).zfill(18)

patterns = defaultdict(int)  # holds keys as strings as and values as int

text = int(raw_input())

for _ in range(text):
    operation, number = raw_input().strip().split()

    if operation == '+':
        pattern = getSPattern(number)
        patterns[pattern] += 1
    elif operation == '-':
        pattern = getSPattern(number)
        patterns[pattern] -= 1
    elif operation == '?':
        print patterns.get(getPattern(number), 0)

【讨论】:

【解决方案2】:

有了@cdlane 已经完成的解释,我只需要添加我对getSPattern 的重写,我认为大部分时间都花在了那里。根据我最初的评论,这可在https://eval.in/641639

def getSPattern(s):
    patlist = ['0' if c in ['0', '2', '4', '6', '8'] else '1' for c in s]
    return "".join(patlist).zfill(19)

使用 zfill(18) 可能会稍微节省一些时间。

【讨论】:

  • 我会使用patstr = ''.join(chr(0x30 + ord(c)&1) for c in s)
  • @MarkRansom 很酷,我试试看,但不需要使用十六进制:[chr(48+(ord(c)&1)) for c in s]
猜你喜欢
  • 1970-01-01
  • 2021-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-12
  • 2018-01-08
  • 2016-11-27
  • 2013-01-26
相关资源
最近更新 更多