我目前正在经历欧拉项目。我已经开始使用JavaScript了,昨天我已经切换到Python,因为我陷入了一个似乎很复杂的问题,用Javascript解决但很容易用Python解决,所以我决定开始从python中的第一个问题开始。
我在问题5中要求我找到可以被1到20之间的所有数字整除的最小正数。
我知道如何用纸和笔来解决它并且我已经使用编程解决了问题,但为了寻求优化它我在欧拉项目的论坛中越过了这个解决方案。
它看起来很优雅,与我的相比它速度相当快,很荒谬,因为解决1到1000号的相同问题需要大约2秒钟,我的需要几分钟。
我试图理解它,但我仍然很难理解它到底在做什么。这是:
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
的用户发布答案 0 :(得分:6)
该代码正在计算从1
到20
(或您使用的任何其他range
)中数字的最不常见的多个。
从1
开始,然后对于该范围内的每个数字k
,它检查k
是i
的因子,如果不是(即i % k > 0
1}})它将该数字添加为因子。
但是i *= k
不会产生最少公倍数,因为例如当你有i = 2
和k = 6
时,它就足以成倍增加i
3
i % 6 == 0
j
,因此内部循环找到的数字i*j
最少,k
k
为i % k == 0
。< / p>
最后,范围中的每个数字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
...
都是range(1, 21)
,我们总是添加最小因素才能这样做,因此我们计算了最少数倍。
也许准确显示值的变化有助于理解这个过程:
range(2, 21)
你可以立即注意到一些事情:
1
可以安全地更改为k
,因为j=k
永远不会做任何事情i *= k
都是素数,内部循环在k
结束时结束,最终会在j
中结束。这是因为当i
是素数时它没有因素,所以没有选项可以使k
成为i
的倍数j < k
{1}}。k
乘以数字i
,以便--------------------------------------------------------
-- Table TABLE_NAME
--------------------------------------------------------
的所有因子现在都在ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '------------------------------------------------------
------------------------' at line 1
。答案 1 :(得分:3)
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)
<强>输出强>
232792560
对于num
的小值,此算法与lassevk的算法大致相同。但是当num = 1000
大约快4倍时,num = 2000
大约快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)
这是一些Python 2 / Python 3代码,可以进行实际timeit
测试,比较算法的两种变体。在Python 2.6.6上,使用fractions.gcd
的版本慢了约10%,但在Python 3.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
Python 2.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]
Python 3.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]