如何优化这些嵌套循环?

时间:2013-02-26 16:22:59

标签: python optimization performance

任何人都可以优化此代码块吗?它正在运行,但运行速度很慢。

maxsat = 0
possiblevotes = []
for i in range(1,int(numcats)+1):
    for j in range(1,int(numdogs)+1):
        possiblevotes.append('C' + str(i) + ' ' + 'D' + str(j))
        possiblevotes.append('D' + str(j) + ' ' + 'C' + str(i))
for m in possiblevotes:
    count = 0
    for n in votes:
        if m == n:
            count += 1
        elif m.split()[0] == n.split()[0]:
            count += 1
    if count > maxsat:
        maxsat = count

4 个答案:

答案 0 :(得分:1)

无需生成所有可能的投票。您可以测试实际投票而无需生成possiblevotes列表,因为您可以轻松计算现有投票是否可行。

你也只是真正计算'留下'的选票。您寻找匹配的“留下来”投票并不重要,因为m == n为真的任何“留下来”投票,m.split()[0] == n.split()[0] 为真。所以你可以放弃第一个计数,只看第二个。

现在您只是找到stay票的最大数量。使用collections.Counter()可以更轻松地计算:

import collections

vote_counts = collections.Counter(v.split()[0] for v in votes)

maxsat = vote_counts.most_common(1)[0][1]  # retrieve the most popular count

这会计算您的代码计算的相同数字,但现在我们只需要遍历一次的投票,并且只计算'留'投票。

将此与您的循环进行对比,首先循环numcats * numdogs次,然后循环numcats * numdogs * 2 * len(votes)次。因果3 * numcats * numdogs 更大

如果您必须先验证投票,可以使用:

from itertools import ifilter

numcats = int(numcats)
numdogs = int(numdogs)

def validvote(vote):
    stay, go = vote.split()
    cat, dog = sorted((stay, go))
    if (cat[0], dog[0]) != ('C', 'D'):
        return False
    if not (1 >= int(cat[1:]) >= numcats):
        return False
    if not (1 >= int(dog[1:]) >= numdogs):
        return False
    return True

vote_counts = collections.Counter(v.split()[0] for v in ifilter(validvote, votes))

您也可以开始使用 go 投票:

stay_votes = collections.Counter()
go_votes = collections.Counter()

for vote in ifilter(validvote, votes):
    stay, go = vote.split()
    stay_votes[stay] += 1
    go_votes[go] += 1

现在你可以简单地减去来自逗留投票数的投票数(任何数据都会被删除):

total_votes = stay_votes - go_votes

# Display top 10
for creature, tally in total_votes.most_common(10):
    print('{}: {:>#5d}'.format(creature, tally))

当然,你也可以一次性完成计算:

total_votes = collections.Counter()

for vote in ifilter(validvote, votes):
    stay, go = vote.split()
    total_votes[stay] += 1
    total_votes[go] -= 1

但保持投票结果分开可能对以后的分析很有意思。

答案 1 :(得分:0)

使用字典而不是列表:

possiblevotes = {}
for i in range(1,int(numcats)+1):
    for j in range(1,int(numdogs)+1):
        possiblevotes['C' + str(i) + ' ' + 'D' + str(j)] = 0
        possiblevotes['D' + str(j) + ' ' + 'C' + str(i)] = 0
for n in votes:
    possiblevotes[n] += 1
....

答案 2 :(得分:0)

由于嵌套循环,代码需要很长时间。如果你有1000只猫,1000只狗和1000票,那么第一组循环运行1000x1000次;第二组运行1000x1000x1000次。如果我们可以删除嵌套循环,那么代码将运行得更快。

我注意到你似乎记录了投票,其中'C1 D3'与'D3 C1'相同。我建议你使用collections模块中的Counter类来完成繁重的工作。这是我的解决方案:

import collections

if __name__ == '__main__':
    votes = ['C1 D3', 'D1 C5', 'D3 C1', 'd1 c1', 'c1 d3'] # Example votes

    # Normalize the votes: 'D3 C1' becomes 'C1 D3',
    # 'c1 d3' becomes 'C1 D3'
    normalized_votes = [''.join(sorted(v.upper().split())) for v in votes]

    # Count the votes
    counter = collections.Counter(normalized_votes)

    # Top 10
    print '--- TOP 10 ---'
    for vote, count in counter.most_common(10):
        print count, vote

    # Or print all
    print '--- ALL ---'
    for vote, count in counter.iteritems():
        print count, vote

讨论

  • 此解决方案使用4个循环:第一个是派生 normalized_votes ,第二个是创建计数器变量。最后两个循环处理打印结果。这些循环都不是嵌套的。有人可能会争辩说Counter类的实现可能包含嵌套循环,但我相信这个类的实现尽可能高效。

  • 一个重要的步骤是规范投票,这应该大大简化你的统计。虽然我已经在一行中完成了它,但您可以将其分解为几个步骤以帮助理解。

    1. 第一步是将所有投票转换为大写:'d3 c1'变为'D3 C1'。
    2. 接下来,我将它们分解为具有split()功能的列表:'D3 C1'变为['D3','C1']。
    3. 第三步是对每个项目进行排序:['D3','C1']变为['C1','D3']
    4. 最后一步将它们粘合在一起:['C1','D3']变成'C1 D3'

答案 3 :(得分:0)

import re

vote_pattern = re.compile('^(C|D)\d+\s')

votes = ['123', 'A1123', 'cC32', 'C', 'D0', 'C11']
maxsat = sum(0 if vote_pattern.match(vote) is None else 1 for vote in votes)

当然,你可以将这个可怕的数字改为过滤器。