python在for循环方面的效率是难以置信的低,还是仅仅是我的代码?

python在for循环方面的效率是难以置信的低,还是仅仅是我的代码?,python,python-3.x,algorithm,pythagorean,Python,Python 3.x,Algorithm,Pythagorean,我的问题不是关于我的代码,而是关于python的。我们家有一场编码竞赛,我父母分别用C#和javascript编写了这个项目的版本。我认为python应该和其他语言一样高效,如果不是比其他语言更高效的话。我们最终编写了相同的代码(显然语法不同),但它们的文件在几毫秒内运行(我爸爸用Javascript写221个,我妈妈用C#写500个),我的文件在大约5分钟内运行。这是一个荒谬的区别,让我质疑python究竟是如何应用于现实世界的数据处理和算法求解的 我用毕达哥拉斯数三元组解决了一个问题。问题

我的问题不是关于我的代码,而是关于python的。我们家有一场编码竞赛,我父母分别用C#和javascript编写了这个项目的版本。我认为python应该和其他语言一样高效,如果不是比其他语言更高效的话。我们最终编写了相同的代码(显然语法不同),但它们的文件在几毫秒内运行(我爸爸用Javascript写221个,我妈妈用C#写500个),我的文件在大约5分钟内运行。这是一个荒谬的区别,让我质疑python究竟是如何应用于现实世界的数据处理和算法求解的


我用毕达哥拉斯数三元组解决了一个问题。问题是,如果a的平方+b的平方=c的平方,那么只有a,b和c的一个组合加起来就是一千。最后,打印制作1000所需的数字,分次打印在一起

for c in range(1000, 0, -1):
        for a in range(1000, 0, -1):
            for b in range(1000, 0, -1):
                if (a*a)+(b*b)==(c*c):
                    if a+b+c == 1000:
                        print("I have found it")
                        print(a*b*c)
                        quit()

与静态类型语言相比,Python通常效率较低。除了动态类型化之外,Python在其他几个方面都是动态的,比如某些操作的长链回退,
用于任何iterable上的循环,
,上下文管理器,等等,这些都可以用于非常广泛的对象集。这些特性使编程变得方便,但通常会带来一定的成本。因此,优化的Python程序将经常被C++、Haskell、java等程序中的一个等效程序优于

但是我认为主要的问题是你的算法不是很有效。这里有三个循环,每个循环的范围超过999项。这意味着内部循环最多执行997'002'999次。我们可以重写算法,使其最多需要499'500次迭代,如下所示:

for c in range(1000, 0, -1):
    for a in range(999-c, 0, -1):
        b = 1000-a-c
        if a*a + b*b == c*c:
            print("I have found it")
            print(a*b*c)
            quit()

因此,这将在27.6毫秒内运行。

通过使用毕达哥拉斯三元组的平方和关系,您可以进一步加快该过程。在这个实现中可以看到

我自己用下面的代码进行了测试,发现它可以在十分之二毫秒内运行

import math
import time

def pythagoreanTriplets(limits) : 
    c, m = 0, 2

    # Limiting c would limit  
    # all a, b and c 
    while c < limits : 

        # Now loop on n from 1 to m-1 
        for n in range(1, m) : 
            a = m * m - n * n 
            b = 2 * m * n 
            c = m * m + n * n 

            # if c is greater than 
            # limit then break it 
            if limits%(a+b+c) ==0: 
              #print(1000/(a+b+c)*a,1000/(a+b+c)*b,1000/(a+b+c)*c, "Found")
              return

            print(a, b, c) 

        m = m + 1

start = time.process_time() 
pythagoreanTriplets(1000)
elapsed = time.process_time() 
elapsed = elapsed - start
print("Time spent in (function name) is: ", elapsed)
导入数学
导入时间
def毕达哥拉斯三联体(限值):
c、 m=0,2
#限制c将限制
#全部a、b和c
而c<限制:
#现在在n上从1循环到m-1
对于范围(1,m)内的n:
a=m*m-n*n
b=2*m*n
c=m*m+n*n
#如果c大于
#限制然后打破它
如果限值%(a+b+c)==0:
#打印(1000/(a+b+c)*a,1000/(a+b+c)*b,1000/(a+b+c)*c,“已找到”)
回来
印刷品(a、b、c)
m=m+1
开始=时间。处理时间()
毕达哥拉斯三胞胎(1000)
已用时间=时间。处理时间()
已用=已用-开始
打印(“在(函数名)中花费的时间:”,已用)
正如威廉所说,高效的代码是这里最大的因素。

用Pete的见解更新了上述代码

我用print语句运行了一次代码以确保它的正确性,一次没有看到速度,它显示了大约20倍的下降


我们可以使Python for循环更快

发帖人的问题是为什么Python“for循环”相对于C#和JavaScript速度如此之慢。提出一种不同的算法来减少对“for循环”的需求并不能解决这一问题(特别是因为C#和JavaScript版本使用修改后的算法也会更快)。在一般情况下,编程语言是使用类似的算法进行比较的,它们的区别是由允许它们在一项任务中表现出色的语言特性实现的--

在本例中,任务是使用Python语言功能更快地搜索超过10亿个数字(三个嵌套for循环的大小)

尝试了两种方法来加速代码:Cython和Numba(分别)

设置:

  • Jupyter笔记本
  • Python 3.5.2操作系统
  • Windows 10 64位
  • 处理器i7
结果:

  • Python 3.5.2=>387.356秒
  • Cython=>117.223秒
  • Cython(改进)=>0.63秒(使用@codesculator建议)
  • Numba=>0.5秒
  • Numba版本可与海报的JavaScript和C#times相媲美。 使用相同的“次优”算法,Numba在纯Python上提供了774的速度提升。请注意,这三个实现使用的代码基本相同,如下所示

  • Python 3.5.2代码
  • Python 3.5.2输出

    I have found it 31875000
    Elapsed Time
     387.3550021648407
    
  • Cython代码(请参阅)
  • Cython输出

    I have found it 31875000
    ('Elapsed Time\n', 117.22299981117249)
    
  • Cython(带Cdef)

    %%cython-a

    导入时间

    cdef int solve(): cdef int a、b、c

    for c in range(1000, 0, -1):
        for a in range(1000, 0, -1):
            for b in range(1000, 0, -1):
                if (a*a)+(b*b)==(c*c):
                    if a+b+c == 1000:
                        return a*b*c
    
    t0=时间。时间() 打印('我找到它{}'。格式(solve()) 打印(“已用时间\n”,Time.Time()-t0)

  • 输出(使用Cdef的Cython)

  • Numba代码(请参阅)
  • 麻木产量

    I have found it 31875000
    Elapsed Time
     0.5209996700286865
    

    似乎有很多重复的计算可以从循环中取出,所以我决定预先计算平方。
    sqrs
    作为dict执行三项任务:

  • 它只需迭代
    sqrs.items()
    ,就可以循环x和x**2
  • 它允许查找**2+b**2本身是否为正方形
  • 如果上一个任务为true,则允许查找
    c
  • 以下是计时代码:

    def original():
        for c in range(1000, 0, -1):
            for a in range(1000, 0, -1):
                for b in range(1000, 0, -1):
                    if (a*a)+(b*b)==(c*c):
                        if a+b+c == 1000:
                            print("I have found it")
                            print(a*b*c)
                            return
    
    %time original()
    I have found it
    31875000
    Wall time: 2min 27s
    
    def pre_compute_sqrs():
        sqrs = {x**2:x for x in range(1, 1001)}
        for aa, a in sqrs.items():
            for bb, b in sqrs.items():
                ab, aabb = a + b, aa + bb
                if ab >= 1000 or aabb >= 1_000_000:
                    break
                if aabb not in sqrs:
                    continue
                c = sqrs[aabb]
                if a+b+c == 1000:
                    print("I have found it")
                    print(a*b*c)
                    return
    
    %time pre_compute_sqrs()
    I have found it
    31875000
    Wall time: 46.9 ms
    
    Python速度慢吗?可能是


    通常是不是太慢了?不,通常不会。

    以上内容将总共运行10亿次迭代。以上不是找到解决方案的有效方法。你爸爸用另一种方法解决了这个问题。。。但是你的问题的答案是Python很慢。。。但并不是说你在毫无必要地重新计算a^2和c^2。当每个人问我的父母是否使用了相同的算法时,他们确实这么做了。但python的速度仍然慢得惊人!感谢到目前为止的所有答案:)要在python中进行数据处理,您(1)使用更智能的算法(您在这里进行了大量冗余计算)和(2)d
    for c in range(1000, 0, -1):
        for a in range(1000, 0, -1):
            for b in range(1000, 0, -1):
                if (a*a)+(b*b)==(c*c):
                    if a+b+c == 1000:
                        return a*b*c
    
    I have found it 31875000
    ('Elapsed Time\n', 0.6340005397796631)
    
    
        import time
        from numba import jit
    
        @jit(nopython=True)    # only need to add Numba decorator to get speed up
        def solve():
            for c in range(1000, 0, -1):
                for a in range(1000, 0, -1):
                    for b in range(1000, 0, -1):
                        if (a*a)+(b*b)==(c*c):
                            if a+b+c == 1000:
                                return a*b*c
    
    
        t0 = time.time()
        print('I have found it {}'.format(solve()))
        print("Elapsed Time\n", time.time()- t0)
    
    I have found it 31875000
    Elapsed Time
     0.5209996700286865
    
    def original():
        for c in range(1000, 0, -1):
            for a in range(1000, 0, -1):
                for b in range(1000, 0, -1):
                    if (a*a)+(b*b)==(c*c):
                        if a+b+c == 1000:
                            print("I have found it")
                            print(a*b*c)
                            return
    
    %time original()
    I have found it
    31875000
    Wall time: 2min 27s
    
    def pre_compute_sqrs():
        sqrs = {x**2:x for x in range(1, 1001)}
        for aa, a in sqrs.items():
            for bb, b in sqrs.items():
                ab, aabb = a + b, aa + bb
                if ab >= 1000 or aabb >= 1_000_000:
                    break
                if aabb not in sqrs:
                    continue
                c = sqrs[aabb]
                if a+b+c == 1000:
                    print("I have found it")
                    print(a*b*c)
                    return
    
    %time pre_compute_sqrs()
    I have found it
    31875000
    Wall time: 46.9 ms