【问题标题】:How can I optimize this code??(python)我怎样才能优化这段代码??(python)
【发布时间】:2018-08-05 19:10:19
【问题描述】:

这是Project Euler网站的问题5。

def divisable_into_1to20(num):
    for i in range(11,21):
        if num % i != 0:
        return False
    return True 

for i in range(2520,10000000000):
    if divisable_into_1to20(i):
        print(i)
        break

需要 140 秒

【问题讨论】:

  • 这个问题的预期输出是什么?
  • 他们;大概希望你在数学上考虑清楚,然后计算并乘以因子,而不是一一检查每个数字。
  • 例如:能被1、2、3整除的最小数是1*2*3=6。能被 1、2、3 和 4 整除的最小数不是 24,而是 12。为什么?因为 4 已经处理了 2(并且 1 已经处理好了),所以它是 3*4 如果你能弄清楚如何向 5 岁的孩子解释这个逻辑,那么你可以把这个逻辑变成代码,你可以得到前20个数字的最小公倍数。
  • 如果此代码在如此短的时间(140 秒)内运行并给出正确答案,则您可以访问 ProjectEuler 讨论页面来解决此问题。在那里您可以看到其他方法,包括 Python 中的一些方法。为什么不适合你? ProjectEuler 明确表示,您不应在此类网站上寻求帮助。
  • 作为一个投影机爱好者,我必须说我真的不喜欢那些在不是投影机论坛的其他网站上发布投影机答案(非最佳或最佳)的人,这样做不仅会违背projecteuler“规则”,但你也可能为那些没有找到答案的人偷走一些乐趣......当然,人们可以选择不看......但即便如此,我认为这是错误的,所以我们开始吧,我对你的问题投了反对票:)。请务必仔细阅读https://projecteuler.net/about

标签: python optimization


【解决方案1】:

假设您希望代码速度快。我的建议是使用 Cython。快速搜索它的documentation 可以告诉您许多高度优化代码的技巧:声明类型、并行化...... Jupyter Notebook 中的一个示例可能是:

    %%cython
    # distutils: language = c++
    cimport cython
    from libcpp cimport bool

    @cython.cdivision(True)
    cdef inline bool divisable_into_1to20(int num):
        cdef int i, a=11, b=21
        for i in range(a,b):
            if cython.cmod(num,i):
                return False
        return True 

    cpdef problem_5_proj_euler():
        cdef int num, k1=2520, k2=10000000000
        for num in range(k1,k2):
            if divisable_into_1to20(num):
                return num

原始解决方案在我的笔记本电脑上花费了 130 秒。这需要 900ms

奖励:并行化!

有人可能会说:嘿!当然,如果我们并行化,我们将获得更好的性能!所以我尝试了:

    %%cython
    # distutils: language = c++
    cimport cython
    from cython.parallel import prange
    from libcpp cimport bool

    @cython.cdivision(True)
    cdef inline bool divisable_into_1to20(int num) nogil:
        cdef int i, a=11, b=21
        for i in range(a,b):
            if cython.cmod(num,i):
                return False
        return True 

    cpdef problem_5_proj_euler():
        cdef int final_num, num, k1=2520, k2=10000000000
        for num in prange(k1,k2, nogil=True):
            if divisable_into_1to20(num):
                break
        return num

令人惊讶的是,这个解决方案需要 1.9 秒,速度是原来的两倍!鉴于任务规模较小,并行化会导致创建线程和运行代码片段的开销。

【讨论】:

  • 看来您已经回答了自己的问题。没有什么比得上“啊哈!”当你终于解决了一个你已经研究了一段时间的问题的那一刻。通常是出于好意,希望分享我们的见解,以便其他人也能享受那一刻。然而,遗憾的是,对于您的读者来说,情况并非如此。真正的学习是一个积极的过程,看到它是如何完成的,距离体验发现的顿悟还有很长的路要走。请不要否认别人你如此珍视自己的东西。
  • 我会收回我的反对票,因为现在答案似乎集中在优化一段代码上......不过,我认为像这样的线程是错误的,并且违背了projecteuler 的性质,无论如何...只是说:/
【解决方案2】:

(几乎)最快的解决方案如下:

print(5*7*9*11*13*16*17*19)

很容易看出,一个数可以被 2 到 20 之间的每个数整除当且仅当它是这个数的倍数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-24
    • 2023-03-05
    • 2023-02-09
    • 2017-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多