理解Python中Euler项目的解决方案

2024-06-12 09:51:24 发布

您现在位置:Python中文网/ 问答频道 /正文

我现在正在进行Euler项目。我昨天开始用Python解决一个复杂的问题。在

我在第五题,它要求我找出最小的正数,它可以被1到20的所有数整除。在

我知道如何用纸和铅笔来解决这个问题,我已经用编程解决了,但是为了优化它,我在Euler项目的论坛上交叉了这个解决方案。在

它看起来很优雅,和我的相比,它相当快,可笑的快,因为它需要大约2秒来解决数字1到1000的相同问题,而我的需要几分钟。在

我试着去理解它,但我还是很难理解它到底在做什么。这里是:

i = 1
for k in (range(1, 21)):
    if i % k > 0:
        for j in range(1, 21):
            if (i*j) % k == 0:
                i *= j
                break
print i

它最初是由一个名为lassevk的用户发布的


Tags: 项目inforif编程range数字解决方案
2条回答

该代码正在计算从1到{}(或您使用的任何一个range)的最小公倍数。在

它从1开始,然后对该范围内的每个数字k检查k是否是{}的因子,如果不是(即当i % k > 0时),它将该数作为因子相加。在

但是,做i *= k不会产生最小的公共倍数,因为例如当你有i = 2和{}时,用i乘以{}就足够了,所以内循环会找到最小的数j,这样{}就有{}作为因子。在

最后,在这个范围内的每个数k都是i % k == 0,为了这样做,我们总是加上最小因子,从而计算出最小公倍数。在


也许准确地展示价值观的变化有助于理解这一过程:

i = 1
k = 1
i % k == 0  -> next loop

i = 1
k = 2
i % k == 1 > 0
   j = 1
   if (i*j) % k == 1 -> next inner loop
   j = 2
   if (i*j) % k == 0
      i *= j
i = 2
k = 3
i % k == 2 > 0
    j = 1
    if (i*j) % k == 2 -> next inner loop
    j = 2
    if (i*j) % k == 4 % 3 == 1 -> next inner loop
    j = 3
    if (i*j) % k == 6 % 3 == 0
        i *= j
i = 6
k = 4
i % k == 2 > 0
    j = 1
    if (i*j) % k == 6 % k == 2 -> next inner loop
    j = 2
    if (i*j) % k == 12 % k == 0
        i *= j
i = 12
k = 5
i % k == 2 > 0
    j = 1
    if (i*j) % k == 12 % k == 2 -> next inner loop
    j = 2
    if (i*j) % k == 24 % k == 4 -> next inner loop
    j = 3
    if (i*j) % k == 36 % k == 1 -> next inner loop
    j = 4
    if (i*j) % k == 48 % k == 3 -> next inner loop
    j = 5
    if (i*j) % k == 60 %k == 0
       i *= j
i = 60
...

您可以立即注意到以下几点:

  • 由于1从不执行任何操作,range(1, 21)可以安全地更改为range(2, 21)
  • 每次k都是一个质数,内部循环在j=k时结束,并以i *= k结束。这是意料之中的,因为当k是一个素数时,它没有因子,所以没有一个更小的数j的选择,这会使i成为{}的倍数。在
  • 在其他情况下,数i乘以一个数j < k,因此{}的所有因子现在都在i中。在

Bakuriu通过解释lassevk的“阶乘”算法回答了您的问题。然而,有一种更简单的方法来实现这一点,对于更大的输入来说速度更快。在

num是序列中最高的数字。例如num = 20。在

只需将从2到num的所有数字相乘,并在每一步除以当前乘数和当前乘积的GCD(最大公分母)。在

在这段代码中,我将产品初始化为num,只是为了使循环看起来更好一些。在

num = 20

p = num
for i in range(2, num):
    # Compute GCD(p, i) using Euclid's algorithm
    # When the loop ends, a is the GCD
    a, b = p, i
    while b:
        a, b = b, a % b
    p *= i // a

print(p)

输出

^{pr2}$

对于num的小值,该算法与lassevk算法的时间差不多。但当num = 1000时,它大约快4倍,当{}时,它大约快14倍。在


正如Bakuriu在评论中提到的,fractions模块提供了一个gcd函数。这使得代码稍微缩短了一些,但它并没有在我的测试中提供加速。在

from fractions import gcd

num = 20

p = num
for i in range(2, num):
    p *= i // gcd(p, i)

print(p)

下面是一些python2/python3代码,它们对我的算法的2个变体进行实际的timeit测试。在Python2.6.6上,使用fractions.gcd的版本要慢10%左右,但在Python3.6上,它可以慢5到10倍!这两个测试都是在运行Debian衍生Linux的旧2GHz机器上进行的。在

''' Test the speed of calculating the Least Common Multiple 
    via an inline implementation of Euclid's GCD algorithm
    vs the gcd function from the fractions module

    See http://stackoverflow.com/q/38074440/4014959

    Written by PM 2Ring 2016.06.28
'''

from timeit import Timer
from fractions import gcd

def lcm0(num):
    p = num
    for i in range(2, num):
        a, b = p, i
        while b:
            a, b = b, a % b
        p *= i // a
    return p

def lcm1(num, gcd=gcd):
    p = num
    for i in range(2, num):
        p *= i // gcd(p, i)
    return p

funcs = (lcm0, lcm1)

def time_test(loops, reps):
    ''' Print timing stats for all the functions '''
    for func in funcs:
        fname = func.__name__
        setup = 'from __main__ import num,' + fname
        cmd = fname + '(num)'
        t = Timer(cmd, setup)
        r = t.repeat(reps, loops)
        r.sort()
        print('{0}: {1}'.format(fname, r))

num = 5
loops = 8192
reps = 5
for _ in range(10):
    print('\nnum={0}, loops={1}'.format(num, loops))
    time_test(loops, reps)
    num *= 2
    loops //= 2

Python2.6输出

num=5, loops=8192
lcm0: [0.055649995803833008, 0.057304859161376953, 0.057752132415771484, 0.060063838958740234, 0.064462900161743164]
lcm1: [0.067559003829956055, 0.068048954010009766, 0.068253040313720703, 0.069074153900146484, 0.084647893905639648]

num=10, loops=4096
lcm0: [0.058645963668823242, 0.059965133666992188, 0.060016870498657227, 0.060331821441650391, 0.067235946655273438]
lcm1: [0.072937965393066406, 0.074002981185913086, 0.074270963668823242, 0.074965953826904297, 0.080986976623535156]

num=20, loops=2048
lcm0: [0.063373088836669922, 0.063961029052734375, 0.064354896545410156, 0.071543216705322266, 0.10234284400939941]
lcm1: [0.079973936080932617, 0.080717802047729492, 0.082272052764892578, 0.086506843566894531, 0.11265397071838379]

num=40, loops=1024
lcm0: [0.077324151992797852, 0.077867984771728516, 0.07857513427734375, 0.087296962738037109, 0.10289192199707031]
lcm1: [0.095077037811279297, 0.095172882080078125, 0.095523834228515625, 0.095964193344116211, 0.10543298721313477]

num=80, loops=512
lcm0: [0.09699702262878418, 0.097161054611206055, 0.09722590446472168, 0.099267005920410156, 0.10546517372131348]
lcm1: [0.1151740550994873, 0.11548399925231934, 0.11627888679504395, 0.11672496795654297, 0.12607502937316895]

num=160, loops=256
lcm0: [0.10686612129211426, 0.10825586318969727, 0.10832309722900391, 0.11523914337158203, 0.11636996269226074]
lcm1: [0.12528896331787109, 0.12630200386047363, 0.12688708305358887, 0.12690496444702148, 0.13400888442993164]

num=320, loops=128
lcm0: [0.12498903274536133, 0.12538790702819824, 0.12554287910461426, 0.12600493431091309, 0.13396120071411133]
lcm1: [0.14431190490722656, 0.14435195922851562, 0.15340209007263184, 0.15408897399902344, 0.159912109375]

num=640, loops=64
lcm0: [0.15442395210266113, 0.15479183197021484, 0.15657520294189453, 0.16451501846313477, 0.16749906539916992]
lcm1: [0.17400288581848145, 0.17454099655151367, 0.18450593948364258, 0.18503093719482422, 0.19588208198547363]

num=1280, loops=32
lcm0: [0.21137905120849609, 0.21206808090209961, 0.21211409568786621, 0.21935296058654785, 0.22051215171813965]
lcm1: [0.23439598083496094, 0.23578977584838867, 0.23717594146728516, 0.24761080741882324, 0.2488548755645752]

num=2560, loops=16
lcm0: [0.34246706962585449, 0.34283804893493652, 0.35072207450866699, 0.35794901847839355, 0.38117814064025879]
lcm1: [0.3587038516998291, 0.36004209518432617, 0.36267900466918945, 0.36284589767456055, 0.37285304069519043]

Python3.6输出

num=5, loops=8192
lcm0: [0.0527388129994506, 0.05321520800134749, 0.05394392299785977, 0.0540059859995381, 0.06133090399816865]
lcm1: [0.45663526299904333, 0.4585357750002004, 0.45960231899880455, 0.4768777699973725, 0.48710195899911923]

num=10, loops=4096
lcm0: [0.05494695199740818, 0.057305197002278874, 0.058495635999861406, 0.07243769099659403, 0.07494244600093225]
lcm1: [0.5807856120009092, 0.5809524680007598, 0.5971023489983054, 0.6006399979996786, 0.6021203519994742]

num=20, loops=2048
lcm0: [0.06225249999988591, 0.06330173400056083, 0.06348088900267612, 0.0639248730003601, 0.07240132099832408]
lcm1: [0.6462642230035271, 0.6486189150018618, 0.6605903060008131, 0.6669839690002846, 0.7464891349991376]

num=40, loops=1024
lcm0: [0.06812337999872398, 0.06989315700047882, 0.07142737200047122, 0.07237963000079617, 0.07640906400047243]
lcm1: [0.6938937240011, 0.7021358079982747, 0.7238045579979371, 0.7265497620028327, 0.7266306150013406]

num=80, loops=512
lcm0: [0.07672808099960093, 0.07784233300117194, 0.07959756200216361, 0.08742279999933089, 0.09116945599816972]
lcm1: [0.7249167879999732, 0.7272519250000187, 0.7329213439988962, 0.7570086350024212, 0.75942590500199]

num=160, loops=256
lcm0: [0.08417846500015003, 0.08528995099914027, 0.0856771619983192, 0.08571110499906354, 0.09348897000018042]
lcm1: [0.7382230039984279, 0.7425414600002114, 0.7439042109981528, 0.7505959240006632, 0.756812355000875]

num=320, loops=128
lcm0: [0.10246147399811889, 0.10322481399998651, 0.10324400399986189, 0.10347093499876792, 0.11325025699989055]
lcm1: [0.7649764790003246, 0.7903363080004056, 0.7931463940003596, 0.8012050910001562, 0.8284494129984523]

num=640, loops=64
lcm0: [0.13264304200129118, 0.13345745100014028, 0.13389246199949412, 0.14023518899921328, 0.15422578799916664]
lcm1: [0.8085992009982874, 0.8125102049998532, 0.8179558970005019, 0.8299506059993291, 0.9141929620018345]

num=1280, loops=32
lcm0: [0.19097876199884922, 0.19147844200051622, 0.19308012399778818, 0.19317538399991463, 0.20103917100277613]
lcm1: [0.8671656119986437, 0.8713741569990816, 0.8904907689975516, 0.9020749549999891, 0.9131527989993629]

num=2560, loops=16
lcm0: [0.3099351109995041, 0.31015214799845126, 0.3101941059976525, 0.32628724800088094, 0.3492128660000162]
lcm1: [0.9883516860027157, 0.988955139000609, 0.9965159560015309, 1.0160803129983833, 1.0170008439999947]

相关问题 更多 >