【问题标题】:Optimizing python for loops优化python for循环
【发布时间】:2010-11-13 08:07:35
【问题描述】:

这里有两个程序可以天真地计算素数的个数 一个是 Python,另一个是 Java。

public class prime{
    public static void main(String args[]){
        int n = Integer.parseInt(args[0]);
        int nps = 0;
 boolean isp;

        for(int i = 1; i <= n; i++){
            isp = true;

            for(int k = 2; k < i; k++){
               if( (i*1.0 / k) == (i/k) ) isp = false;
            }
            if(isp){nps++;}
 }
        System.out.println(nps);
    }
}


`#!/usr/bin/python`                                                                                                                                        
import sys
n = int(sys.argv[1])
nps = 0

for i in range(1,n+1):
    isp = True
    for k in range(2,i):
        if( (i*1.0 / k) == (i/k) ): isp = False
    if isp == True: nps = nps + 1
print nps

在 n=10000 上运行它们,我得到以下时间。
shell:~$ time python prime.py 10000 && time java prime 10000
1230

真正的 0m49.833s
用户 0m49.815s
系统 0m0.012s
1230

真正的 0m1.491s
用户 0m1.468s
系统 0m0.016s

我在这里以不正确的方式在 python 中使用 for 循环还是 python 实际上慢了这么多?

我不是在寻找专门为计算素数而设计的答案,而是我想知道 Python 代码是否通常以更智能的方式使用。

Java 代码是用 javac 1.6.0_20
使用 java 版本“1.6.0_18”运行
OpenJDK 运行时环境 (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1~9.10.1) OpenJDK Client VM(build 16.0-b13,混合模式,共享)

Python 是:
Python 2.6.4(r264:75706,2009 年 12 月 7 日,18:45:15)

【问题讨论】:

  • 您可能想要做的一件显而易见的事情是将range 更改为xrange - 应该会对内存使用产生一些影响,可能还会影响速度。
  • 一般来说,将i 除以i 的平方根就足够了。 Sieve of Eratosthenes 可能更好,但这不是问题的范围;)
  • 而不是“for i in range(a, b) doStuff();”尝试类似“i = a ; while (i
  • @Laurențiu:Wat?即使这稍微慢一些(我非常怀疑,范围是用 C 编写的),它绝对不是惯用的。
  • @Laurențiu, delnan:不仅如此,代码是错误的。 for i in range(a, b)while i &lt;= b 不一样;与while i &lt; b 相同。这会告诉您有关原始建议的所有信息。

标签: python


【解决方案1】:

正如已经指出的那样,直接的 Python 真的不适合这种事情。质数检查算法是否幼稚也不是重点。但是,通过两件简单的事情,我能够在使用原始算法的同时大大减少 Python 的时间。

首先,将所有内容都放在一个函数中,命名为main() 或其他名称。这将我在 Python 机器上的时间从 20.6 秒减少到 14.54 秒。在全局范围内做事情比在函数中做事情要慢。

其次,使用 Psyco,一个 JIT 编译器。这需要在文件顶部添加两行(当然还要安装 psyco):

import psyco
psyco.full()

这使最后的时间达到了 2.77 秒。

最后一点。我决定在这方面使用 Cython,并将时间降低到 0.8533。但是,我不建议临时用户了解如何进行一些更改以使其快速 Cython 代码。

【讨论】:

  • +1 显示了一个人可以非常轻松地从 Python 中获得多少性能(86%!)。
  • 添加 break 语句,进行整数模测试,使用 xrange 代替 range,将其移动到函数中,并迭代到平方根使我的机器上的运行时间从 17.4 秒缩短到 0.037 秒 - 加速了 470 倍没有 Psyco。在不改变原始算法(无中断或 sqrt)的情况下,但在函数中使用模数和 xrange,它可以降低到 4.6 秒,加速了 3.7 倍。
  • @Russell,你错过了我认为的问题的重点。这是关于比较相同算法的 Java 实现与 Python 实现。
  • 谢谢贾斯汀,我想从中得出的底线是......使用心理:)
  • 不,我明白了 q 的意思。只是认为这是有趣的辅助数据。
【解决方案2】:

是的,Python 很慢,比 C 慢大约一百倍。您可以使用xrange 代替 range 来实现小幅加速,但除此之外它很好。

最终你做错的事情是你用普通的 Python 来做这件事,而不是使用优化的库,比如 Numpy 或 Psyco。

Java 带有一个 jit 编译器,它可以在您只是处理数字的情况下产生很大的不同。

【讨论】:

  • "大约比 C 慢一百倍" ...在有些人为的数字运算比赛中。当您的任务 I/O 繁重时,C 的速度会慢很多;)除此之外:如果您有一个可以在 C 中相对轻松有效地解决的问题,您可以使用 Cython (cython.org)。它将从 Python 方言生成 C 代码,添加可选的静态类型(对于性能提升至关重要,但在与 Python 对话的模块部分中确实是可选的)。
  • numpy and psycho in action - 他们真的快...
【解决方案3】:

替换那个复杂的测试,你可以让你的 Python 快两倍
if i % k == 0: isp = False

您还可以通过在 isp = False 之后添加一个中断来使其速度提高大约八倍(对于 n=10000)。

另外,请帮自己一个忙,跳过偶数(在 nps 上加一以开始包含 2)。

最后,你只需要 k 上升到 sqrt(i)。

当然,如果您在 Java 中进行相同的更改,它仍然比优化后的 Python 快 10 倍左右。

【讨论】:

  • 而且测试2和当前数的平方根之间的数字就足够了。
【解决方案4】:

男孩,当你说这是一个幼稚的实现时,你肯定不是在开玩笑!

但是,在将 JIT 编译、优化的机器代码与解释性语言进行比较时,性能上存在一到两个数量级的差异并不意外。另一个 Python 实现(例如在 Java VM 上运行的 Jython)可能会更快地完成这项任务。你可以试一试。 Cython 允许您向 Python 添加静态类型并在某些情况下获得类似 C 的性能,它可能也值得研究。

即使考虑到标准的 Python 解释器 CPython,问题是:Python 的速度是否足以应付手头的任务?用 Python 这样的动态语言编写代码所节省的时间会弥补运行它所花费的额外时间吗?如果您必须用 Java 编写一个给定的程序,是否会觉得工作量太大而值得麻烦?

考虑一下,例如,在现代计算机上运行的 Python 程序将与在 10 年前的计算机上运行的 Java 程序一样快。你十年前的电脑已经足够快了,不是吗?

Python 确实有许多功能使其非常适合数值工作。其中包括支持无限位数的整数类型、无限精度的小数类型以及专门用于计算的名为 NumPy 的可选库。然而,执行速度通常不是它的主要声名之一。它的优势在于让计算机以最小的认知摩擦做你想做的事。

【讨论】:

  • 我怀疑 Jython(或 IronPython)会达到 Java(或 C#)的速度。是的,它们具有相同的 JIT,但它们仍然必须处理所有的动态性等,这首先是动态语言如此缓慢的主要原因 - “传统”(静态类型)优化仅适用于它们一定程度。
  • 是的,经过反思,我认为 Jython 不会像 Java 那样快,但它仍然可能比 CPython 快,后者是一个直接解释器。 Cython(增加了可选的静态性)可能会做得更好。
  • 我也想到了 Cython。我什至认为 Cython 可以在这里击败 Java。是的,至少 IronPython 在许多情况下比 CPython 表现更好(CPython 是字节码 VM,而不是普通的解释器)。
  • 对,“直截了当的解释器”我的意思是说 CPython VM 不会编译为机器代码,而不是 CPython 直接解释源代码。字节码被解释。
【解决方案5】:

如果您希望快速完成,Python 可能不是前进的方向,但您可以加快速度。首先,您正在使用一种非常缓慢的方法来测试可分性。模数更快。您也可以在检测到匹配时立即停止内部循环(使用 k)。我会做这样的事情:

nps = 0
for i in range(1, n+1):
    if all(i % k for k in range(2, i)): # i.e. if divisible by none of them
       nps += 1

对我来说,这将它从 25 秒缩短到 1.5 秒。使用 xrange 可以将其缩短到 0.9 秒。

您可以通过保留您已经找到的素数列表来进一步加快速度,并且只测试这些素数,而不是直到 i 的每个数字(如果 i 不能被 2 整除,它就不能被4、6、8...)。

【讨论】:

    【解决方案6】:

    你为什么不发布一些关于内存使用的信息——而不仅仅是速度?试图在 tomcat 上获取一个简单的 servlet 在我的服务器上浪费了 3GB。

    你用上面的例子做的不是很好。你需要使用 numpy.用 while 循环替换 for/range,从而避免创建列表。

    最后,python 非常适合进行数字运算,至少对于那些做对了的人来说,并且知道什么是埃拉托色尼筛法,或者 mod 运算是什么。

    【讨论】:

    • 是的,正确的算法确实有帮助——尽管它们是独立于语言的。但是,是的,如果你想在 Python 中处理数字,你可以使用 NumPy 和/或 psyco 和/或 Cython。
    【解决方案7】:

    您可以对这个算法做很多事情来加快它的速度,但其中大多数也会加快 Java 版本的速度。其中一些将比 Java 更快地加速 Python,因此它们值得测试。

    以下只是一些更改,可将我的系统上的时间从 11.4 秒提高到 2.8 秒:

    nps = 0 
    for i in range(1,n+1): 
        isp = True 
        for k in range(2,i):
            isp = isp and (i % k != 0)
        if isp: nps = nps + 1 
    print nps 
    

    【讨论】:

      【解决方案8】:

      具有讽刺意味的是,Python 是一种非常适合开发算法的语言。即使是这样的修改算法:

      # See Thomas K for use of all(), many posters for sqrt optimization
      nps = 0
      for i in xrange(1, n+1):
          if all(i % k for k in xrange(2, 1 + int(i ** 0.5))):
             nps += 1
      

      在不到一秒的时间内运行。像这样的代码:

      def eras(n):
          last = n + 1
          sieve = [0,0] + range(2, last)
          sqn = int(round(n ** 0.5))
          it = (i for i in xrange(2, sqn + 1) if sieve[i])
          for i in it:
              sieve[i*i:last:i] = [0] * (n//i - i + 1)
          return filter(None, sieve)
      

      还是更快。或者试试these

      问题是,python 通常足够快来设计您的解决方案。如果它对生产来说不够快,请使用 numpy 或 Jython 来提高性能。或者将其转换为编译语言,随身携带在 python 中学习的算法观察结果。

      【讨论】:

        【解决方案9】:

        是的,Python 是您会遇到的最慢的实用语言之一。 While 循环比 for i in xrange() 稍快,但最终 Python 总是比其他任何东西都要慢得多。

        Python 有它的位置:原型设计理论和想法,或者在任何情况下,快速生成代码的能力比代码的性能更重要。

        Python 是一种脚本语言。不是编程语言。

        【讨论】:

        • Python 是一种编程语言。 但它并不专注于数字运算速度,是的。此外,Ruby 和许多其他动态语言的性能与 Python 没有太大区别。不过 Lua 可能会更快。
        • -1 表示“Python 是一种脚本语言。不是编程语言”。除非您使用 numpy,否则它不是一种数字运算语言。它是一种比 java 更强大、更有表现力的编程语言。
        • 这很有趣,我预测至少有一个 java 克隆会声称 python 不是一种真正的编程语言来响应这个线程。关于 java 的事情是,一切都可以用真正的语言做得更好。有时,您只需要速度,在这种情况下,您可以在 java 中工作。例如,java 甚至没有闭包。当它甚至没有适当的词法作用域时,你如何称它为“真正的”语言?
        • 好吧,如果我们要追求原始性能,我们也不会使用 Java。我们将使用完全编译的语言,例如 C. Horses 用于课程。对于许多程序来说,计算素数是无关紧要的。也就是说,经过一些调整,我上面的答案在一秒钟内完成了测试用例。
        • 伙计们,伙计们。剪掉语言小便比赛。我之所以评论是因为“X 不是一种编程语言”的概念,因为它是动态的/解释的/任何荒谬的(我不喜欢 PHP 的许多“设计”方面,但它仍然是一种编程语言)。此外,由于 Python 是一种通用语言,熟练的程序员可以很好地解决大多数问题,节省系统编程和数字运算。时期。另外——尽管我不喜欢 Java 被限制在 OOP 的一个子集中——你肯定可以在其中完成很多事情。
        猜你喜欢
        • 2015-04-15
        • 2020-11-07
        • 2017-02-21
        • 2017-01-17
        • 2017-05-28
        • 1970-01-01
        • 2011-08-30
        • 1970-01-01
        相关资源
        最近更新 更多