为什么在相同的Python / Java代码之间存在如此巨大的性能差异?

时间:2012-10-24 10:41:22

标签: java python performance

我目前正在尝试使用Python解决Project Euler问题(我今天才提到的)。我正在工作的问题是问题5,其中

  

2520是可以除以1到10之间的每个数字的最小数字,没有任何余数。

     

从1到20的所有数字均可被整除的最小正数是多少?

之前我已经解决了使用Java的问题,所以使用与之前相同的方法,我只是创建了一个迭代的循环,但似乎我的代码永远不会结束。

的Python:

i = 1
while 1:
    if i%2 == 0 and i%3==0 and i%4==0 and i%5==0 and i%6==0 and i%7==0 and i%8==0 and i%9==0 and i%10==0 and i%11==0 and i%12==0 and i%13==0 and i%14==0 and i%15==0 and i%16==0 and i%17==0 and i%18==0 and i%19==0:
        print i
        break
    i+=1

爪哇:

public class p5
{
    public static void main(String[] args)
    {
        for (int i=1;;i++)
        {
            if (i%1==0&&i%2==0&&i%3==0&&i%4==0&&i%5==0&&i%6==0&&i%7==0&&i%8==0&&i%9==0&&i%10==0&&i%11==0&&i%12==0&&i%13==0&&i%14==0&&i%15==0&&i%16==0&&i%17==0&&i%18==0&&i%19==0&&i%20==0)
            {
                System.out.println(i);
                break;
            }
        }
    }
}

Java在我的计算机上执行了不到3秒钟,而Python代码似乎永远不会结束。有什么提示吗?

修改

显然我输错了,导致它永远不会结束。然而,即使整个事情写得正确(与我的Java输出相同),它仍然需要 1分20秒,而对于Java,它需要大约1 - 2秒。我做错了吗?或者Python的表现不好(不应该是afaik)

2 个答案:

答案 0 :(得分:4)

原始int s的算术在Java中要快得多,然后在CPython中使用完整的整数对象,即你所看到的30倍的性能差异对你的代码来说并不奇怪。

请注意,该算法效率非常低,并且对于稍大的输入不起作用,例如,对于1到50的数字(它需要太长时间,并且正确的结果大于Java中的max int / long)。

使用更高效的算法,我需要〜100 micro - 秒来计算机器上1到100之间所有数字的结果:

#!/usr/bin/env python
from functools import reduce

def gcd(a, b):
    """Greatest common divisor (factor)."""
    while b: # Euclid's algorithm
        a, b = b, a % b
    return a

def lcm(*args):
    """Least common multiple."""
    # lcm(a, b, c) == lcm(lcm(a, b), c)
    return reduce(lambda a, b: a * b // gcd(a, b), args)

def f(n):
    """Smallest positive number evenly divisible by all numbers from 1
       to n including.
    """
    return lcm(*range(1, n + 1))

print(f(10)) # -> 2520
print(f(50)) # -> 3099044504245996706400

To estimate the performance

#!/usr/bin/env python
import timeit
from functools import partial

def measure():
    for n in [10, 50, 100]:
        print("%d: %5.2f microseconds" % (n, timeit.timeit(partial(f, n),
                                                           number=1000*1000)))
measure()

答案 1 :(得分:2)

使用pypy,我的纯粹强力解决方案需要大约250毫秒:

i = 20
while 1:
    if i%20 == 0 and i%19==0 and i%18==0 and i%17==0 and i%16==0 and i%15==0 and i%14==0 and i%13==0 and i%12==0 and i%11==0:
        print i
        break
    i+=20

更优雅应该使用最大公约数和最小公倍数之类的东西。