对于循环速度或替代解决方案?

时间:2015-05-06 11:19:41

标签: python performance loops for-loop

我知道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相同。

5 个答案:

答案 0 :(得分:3)

最短,也许是最快的方式是使用列表理解:

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

答案 1 :(得分:3)

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

一些提示:

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

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

  • 对于范围内 n 的每个值,if语句可以显示:newList[n] = listA[1-ListB[n]&1]。反转listA的顺序以获取1-的git并保存另一个整数操作。

答案 2 :(得分:1)

使用列表理解似乎缩短了一些时间:

function bones_infinite_loop_init(){
    add_theme_support( 'infinite-scroll', array(
        'type'           => 'scroll',
        'container'      => 'masonry',
        'render'         => 'bones_infinite_scroll_render',
        'footer'         => false
    ) );
}
add_action( 'init', 'bones_infinite_loop_init' );

function bones_infinite_scroll_render() { 
$args = array( 'post_type'=> 'wigwam','post_parent' => 0,'posts_per_page' => 25);
    query_posts( $args );
if (have_posts()) : 
    while (have_posts()) : the_post();
        get_template_part( 'content', get_post_format() );
    endwhile; 
endif;
} 

与:相比:

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

答案 3 :(得分:1)

首先,使用按位和运算符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]

祝你好运!

答案 4 :(得分:0)

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

newList = []


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

或使用列表comp:

 [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

因此,无论是在列表comp还是循环中,索引总是会增加更多开销。当你只想要这两个值时,无意义地不断索引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。