【问题标题】:for loop speed or alternative solution?循环速度或替代解决方案?
【发布时间】:2015-05-06 11:19:40
【问题描述】:

我知道 Python 不是为了速度而构建的,但我想提高以下代码的性能:

listA = [1,2]
listB = [1,2,3,4,5,6,7,8,9,10]

# pre-allocate for speed. Appending empty list is slower?
newList = ['NaN']*len(listB)

# Do I need a loop? Can I use something faster?
for n in xrange(len(listB)):
    if listB[n] % 2 == 1:
        newList[n] = listA[0]
    else:
        newList[n] = listA[1]

我的问题是listB 会变得非常大。

我已经为newList 预先分配了内存并使用了xrange。我相信这些可以显着提高大型列表的速度。

但是我什至需要一个 for 循环,因为每个循环都不依赖于先前的结果。 python有数组类型吗?

我可以拆分 listB 并并行运行类似于 Matlab 中的 parfor 的操作吗?

附加信息: 对于我的问题,随着 listA 变大,listB 会呈指数级增长。

对于 listB 中的每个项目,都需要在 listA 中进行查找。然后执行计算(不需要取模)并将结果附加到 newList。然后我对 newList 进行统计分析(为简单起见取平均值)。 newList 将始终与 listB 的长度相同。

【问题讨论】:

  • 你不需要预先分配任何东西,只需遍历 listB
  • 应该listB[1]listA[1]
  • 是的。我已经更正了。谢谢
  • listB 可以有多大?另外,你真的需要创建一个新列表吗?
  • 所以你是说 listB 会随着迭代而增长?

标签: python performance loops for-loop


【解决方案1】:

最短,也许是最快的方法是使用列表推导:

newList = [listA[1 - x%2] for x in listB]

【讨论】:

    【解决方案2】:

    xrange 的目的不是为了获得速度;其目的是减少内存使用。 range(N)xrange(N) 的区别在于后者不会扩展为大小为 N 的列表,而是扩展为一个小的生成器对象。

    一些提示:

    • 如果您的列表很大,请查看 numpy. Numpy 拥有高效的数组处理算法,并在内部使用原生代码。

    • 模数很慢 (if listB[n] % 2 == 1:)。在这种情况下,最好使用按位运算符 (if ListB[n]&1)。

    • if 语句可以输出:newList[n] = listA[1-ListB[n]&1] 用于范围内的每个 n 值。颠倒listA的顺序得到1-的git并保存另一个整数操作。

    【讨论】:

      【解决方案3】:

      使用列表推导似乎可以节省一些时间:

      listB = [i for i in xrange(1,1000000)]
      start = clock()
      
      listA = [1,2]
      
      for n in xrange(len(listB)):
          if listB[n] % 2 == 1:
              newList[n] = listA[0]
          else:
              newList[n] = listB[1]
      
      print "Time taken = %.5f" % (clock() - start)
      
      >>> 0.21216
      

      相比:

      listB = [i for i in xrange(1,1000000)]
      start = clock()
      
      listA = [1,2]
      
      newList = [listA[0] if i%2 == 1 else listA[1] for i in listB]
      
      print "Time taken = %.5f" % (clock() - start)
      
      >> 0.15658
      

      【讨论】:

        【解决方案4】:

        首先,将模运算符 n % 2 替换为按位与运算符 n & 1。接下来,不是通过索引访问listB,而是直接使用in 遍历其项目。您可以完全删除 listA。这些小的改进应该会稍微加快速度。

        newList = ((n & 1) + 1 for n in listB)
        

        不过,这段代码的真正优势在于它是生成器推导式,而不是列表推导式。虽然这并没有让它更快,但它确实提高了内存效率。话虽如此,它也有一些缺点。你不能访问整个列表,一旦你访问了一个值,它就消失了。如果您只打算遍历 newList 或对 newList 的每个项目执行一些计算,这很好。如果不是,则将newList 设为列表理解:

        newList = [(n & 1) + 1 for n in listB]
        

        祝你好运!

        【讨论】:

        • 该问题专门询问速度,这也仅适用于listA中的值实际上是o和1
        • @PadraicCunningham 如果他/她使用xrange,那么 OP 似乎也担心记忆。如果 OP 询问“改进以下代码的性能”,我会说内存也属于这一类。由于 OP 的问题暗示这是他的实际代码,而不仅仅是他喜欢做的一个简单的例子,我认为假设listA 将始终包含12 是合理的。如果不是,那么 OP 可以对此代码进行细微调整以考虑 listA 的实际值。
        • 附加信息:对于我的问题,随着 listA 变大,listB 会呈指数级增长。,使用 xrange 和索引列表对内存有什么帮助?列表已经存储在内存中
        【解决方案5】:

        只需遍历 listB 并在开始时设置两个变量,而不是重复索引:

        newList = []
        
        
        i, j = listA[0], listA[1]
        for n in listB:
            if n % 2:
                newList.append(i)
            else:
                newList.append(j)
        

        或使用列表组合:

         [i if n % 2 else j for n in listB]
        

        时间安排:

        In [4]: %%timeit
        newList = ['NaN']*len(listB)
        for n in xrange(len(listB)):
            if listB[n] % 2 == 1:
                newList[n] = listA[0]
            else:
                newList[n] = listA[1]
           ...: 
        100000 loops, best of 3: 2.33 µs per loop
        
        In [5]: %%timeit
           ...: i,j = listA[0], listA[1]
           ...: [i if n % 2 else j for n in listB]
           ...: 
        1000000 loops, best of 3: 1.12 µs per loop
        
        In [16]: %%timeit
        ....: newList = []
        ....: i,j = listA[0], listA[1]
        ....: for  n in listB:
        ....:     if n % 2 == 1:
        ....:         newList.append(i)
        ....:     else:
        ....:         newList.append(j)
        ....: 
        1000000 loops, best of 3: 1.88 µs per loop
        
        In [18]: timeit [listA[1 - x%2] for x in listB]
        1000000 loops, best of 3: 1.38 µs per loop
        

        使用 if n & 1 稍微快一点:

        In [11]: %%timeit
        i,j = listA[0], listA[1]
        [i if n & 1 else j for n in listB]
           ....: 
        1000000 loops, best of 3: 1.04 µs per loop
        

        因此,无论是在列表组合还是循环中,索引总是会增加更多开销。当您只需要两个值时,不断索引 listA 是没有意义的。

        如果你想用 cython 更快地编译并且只需键入几个变量会缩短运行时间:

        In [31]: %%cython
           ....: def faster(l1,l2):
           ....:     cdef int i,j,n
           ....:     i, j = l1[0], l2[1]
           ....:     return [i if n & 1 else j for n in l2]
           ....: 
        
        In [32]: 
        
        In [32]: timeit faster(listA,listB)
        1000000 loops, best of 3: 455 ns per loop
        

        如果您要进行大量数值计算,您可能需要进一步研究 cython 和/或 numpy。

        【讨论】:

          猜你喜欢
          • 2021-06-06
          • 1970-01-01
          • 2014-12-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-04-09
          • 2021-03-15
          相关资源
          最近更新 更多