'''
题目出处
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
求最小的某个数的因子包含连续自然数1-20
'''
分析:
从1开始依次递增,直到这个数能被1-20整除。那么这个数是符合条件的。
因为它能被1-20整除,那么肯定也能被1-10整除,所以起始位置可以是2520.
因为需要被1-20整除,那么肯定也能被11, 13, 17, 19整除,那么递增的步长: step = 11*13*17*19
然后得到条例条件的数。此时的起始值是2520 * step
step = 11 * 13 * 17 * 19
start = 2520 * step
target = start
while True:
if target % 12 == 0 and target % 14 == 0 and target % 15 == 0 and \
target % 16 == 0 and target % 18 == 0 and target % 10 == 0:
break
else:
target += step
print target
因为target已经是1-10及11,13,17,19的倍数了,所以在计算的过程中不需要再次计算
这里其实也是求1-20的最小公倍数,求最小公倍数有通用的算法,可以自行查找。
这里用python实现最小公倍数lcm(fir, sec)
def lcm(fir, sec):
mutil = fir * sec
while fir % sec != 0:
fir, sec = sec, fir % sec
return mutil / sec
然后用下面这个语句即可求得1-20的最小公倍数
print reduce(lcm, [i for i in xrange(1, 21)])
函数reduce:reduce(func, seq[,init])
将函数func作用于序列seq,每次取seq中前2个值,计算得到的结果返回到seq中,
继续取值计算,
直到序列中只剩下一个元素,返回该元素
对于采用最小公倍数实现计算,还可以改进,在此不做分析。
这里对于最小公倍数的处理还不好。一般来说,最小公倍数会和最大公约数一起。
这里重新实现:
def lcm(fir, sec):
return fir * sec / gcd(fir, sec)
def gcd(fir, sec):
while fir % sec != 0:
fir, sec = sec, fir % sec
return sec
这样看起来更清晰
对于计算多个数的最小公倍数,一是可以两丙取其最小公倍数,然后合并
二是,取所有数的素因子分解中出现的所有最大的素因子,然后相乘得到最小公倍数
4 = 2 ^2 6 = 2 * 38 = 2^39 = 3^2 10 = 2*5
12 = 3*4 14 = 2*715 = 3*516 = 2^418 = 2*9
20 = 4*5
在这些分解中,素因子分别为:19, 17, 13, 11, 7, 5, 3^2, 2^4
由这些数相乘后,得到结果,即为题目所求。
对于这个算法,可以先求得所有素数,然后对20(这里是20)对素因子取对数并取整,得到素因子的最大指数
然后把这些素数及因子相乘,得到结果。
def isprime(n):
if n < 2: return False
for i in xrange(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
def smallest_multiple(target):
if target < 1: return None
if target < 2: return target
res = 1
for num in xrange(2, target + 1):
if isprime(num):
power = int(math.floor(math.log(target, num)))
res *= (num ** power)
return res
在这里,遍历target以下所有的数,判断是否是素数,若是,那么求得它的最大幂指数,然后与之前得到的结果相乘
最后返回计算的结果
这里在判断素数的时候,重复比较多,可以用另外一种方式来写:
def smallest_multiple(target):
if target < 1: return None
if target < 2: return target
res = 1
numlist = [1] * (target + 1)
numlist[0:1] = [0, 0]
for i in xrange(2, target + 1):
if numlist[i] == 1:
numlist[i+i::i] = [0] * ((target + 1 - i) / i)
power = int(math.floor(math.log(target, i)))
res *= (i ** power)
return res
在计算素数的过程 ,把后面的非素数(当前素数的倍数)去掉一部分,这样计算量会小一些。
若是对于numlist[i+i :: i] = [0] * ((target + 1 - i) / i) 这一步的长度有不理解的,
可以用numlist[i+i :: i] = [0] * len(numlist[i+i :: i]) 这个来替换