【问题标题】:Improve performance on Python nested for loop提高 Python 嵌套 for 循环的性能
【发布时间】:2012-11-25 04:02:07
【问题描述】:

我有两组数字,每组都在我的 Python 脚本中的一个列表中。对于第一个列表中的每个数字,我需要查看第二个列表中是否有任何数字大于它。我只需要 n2 大于 n1 的次数。 (例如,如果numset1[7,2]numset2[6,9],我只需要3) 现在我正在这样做 - 遍历每个 n1 并检查每个 n2 是否大于它:

possibilities = [(n1<n2) for n1 in numset1 for n2 in numset2]
numPossibilities = sum(possibilities)

目前这是我脚本中最慢的部分,尤其是在处理较大的数据集(包含数千个数字的 numset1 和 numset2)时。我确信有一些方法可以提高效率,我只是不确定如何。

【问题讨论】:

  • 如何排序一个列表并使用bisect 模块?
  • 呃,我发誓在我发布答案之前我没有看到你的评论!如果你想发布你的,我会删除我的:)
  • 我也没有看到你的答案:)

标签: python performance loops


【解决方案1】:

numset2 排序,然后遍历numset1,但对numset2 使用二分搜索,例如使用二分法模块:http://docs.python.org/2/library/bisect.html

import bisect
# your code here
numset2.sort()
L = len(numset2)
numPossibilities = sum([bisect.bisect_right(numset2,n1) < L for n1 in numset1])

另请注意,您的原始代码不会计算您在第二句中要求的内容 - 对于numset1 中的每个元素,它会将numset2 中的有多少元素 大于此值元素,而不是是否有符合条件的元素。

要匹配您的原始代码,请执行以下操作:

numPossibilities = sum([L - bisect.bisect_right(numset2,n1) for n1 in numset1])

【讨论】:

  • 我不相信你可以对set进行排序。
  • 我认为选择的变量名称具有误导性——问题表明“我有两组数字,每组都在我的 Python 脚本的列表中。” ...
  • 我认为这段代码 sn-p 实际上不能正常工作。至少,&lt; L 部分是可疑的——您可能希望将二等分点左侧的条目数相加——即 bisect_right 的实际返回值
  • @happydave ,是的,我已经添加了关于此的注释。这取决于您是想要与问题中的结果匹配的代码,还是问题中的描述。
  • @MrE 对不起,我应该用不同的措辞。我的问题中列出的代码可以满足我的需求,因此请遵循代码的功能,而不是我的错误描述。
【解决方案2】:

您的问题是您必须遍历(n1, n2) 的每个组合,并且有len(numset1) * len(numset2) 组合,当numset1numset2 只是相当大时,它变得非常大。

换一种说法,你的算法的运行时间是 O(n^2)(如果 len(numset1) 大约等于 len(numset2)。让我们加快运行时间。:-)

如果我们对列表进行排序,这将变得容易得多。所以让我们对numset1numset2 进行排序。

>>> numset1.sort()
>>> numset2.sort()

现在,比较numset1 的最小元素(称为n1)和numset2 的最小元素(称为n2)。如果n1 更小,那么我们知道numset2 中有len(numset2) 元素比它大。如果n2 更小,我们知道numset1 中没有任何元素比它更小。

现在,我们不想实际删除列表开头的元素,因为这是对 Python 列表的 O(n) 操作。因此,让我们跟踪我们在每个列表中的位置并进行迭代。

>>> n1_idx, n2_idx, accumulator = 0, 0, 0
>>> while n1_idx < len(numset1) and n2_idx < len(numset2):
        if numset1[n1_idx] < numset2[n2_idx]:
            accumulator += len(numset2) - n2_idx
            n1_idx += 1
        else:
            n2_idx += 1

在此操作结束时,我们花费了 O(nlog(n)) 时间对列表进行排序,并花费了 O(n) 时间进行迭代,因此我们的整体运行时复杂度为 O(nlog(n))。

那个,accumulator(n1, n2) 对的数量,其中 n1 &lt; n2

【讨论】:

    【解决方案3】:

    这应该是一个非常有效的实现:

    def get_possibilities(numset1, numset2):
        sortset1 = sorted(numset1)
        sortset2 = sorted(numset2)
        total = 0
        i2 = 0
        for i1, n1 in enumerate(sortset1, 1):
            while sortset2[i2] <= n1:
                i2 += 1
                if i2 >= len(sortset2):
                    # reached end of i2, so just return total now
                    return total
                # current from sortset2 is greater than from sortset1 so far
                total += i1
        # all remaining elements of sortset2 greater than all elements of sortset1
        total += (len(sortset2) - i2 - 1) * len(sortset1)
        return total
    

    这仅对每个集合进行一次迭代,它通过在运行之前对集合进行排序来完成。这允许一些改进的逻辑,因为如果sortset2 中索引i2 的元素大于sortset1 索引i1 的元素,那么它也大于sortset1 中早期索引的所有元素。

    【讨论】:

      猜你喜欢
      • 2019-04-01
      • 2021-09-26
      • 2021-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-29
      • 1970-01-01
      相关资源
      最近更新 更多