简体   繁体   English

获得可被 1 到 n 整除的最小数的最快和最紧凑的方法

[英]Fastest and most compact way to get the smallest number that is divisible by numbers from 1 to n

I tried my attempt at finding the smallest number that is divisible by numbers from 1 to n, and now I'm looking for advice on ways to further compact/make my solution more efficient.我试图找到可被从 1 到 n 的数字整除的最小数字,现在我正在寻找有关如何进一步压缩/使我的解决方案更有效的建议。 It would be pretty cool if there was an O(1) solution too.如果也有 O(1) 的解决方案,那就太酷了。

def get_smallest_number(n):
    """
    returns the smallest number that is divisible by numbers from 1 to n
    """
    divisors = range(1, n+1)
    check_divisible = lambda x: all([x % y == 0 for y in divisors])
    i = 1
    while True:
        if check_divisible(i):
            return i
        i += 1

Mathematically, you are computing the least common multiple of 1, 2, ..., n .从数学上讲,您正在计算1, 2, ..., n的最小公倍数。 lcm is easily derived from gcd , and lcm is an associative operation. lcm很容易从gcd派生出来,并且lcm是一个关联操作。 reduce is useful for applying an associative operation to an interable. reduce可用于将关联操作应用于可交互对象。 We can combine these ideas (as well as improvements due to Mark Dickinson and Eric Postpischil in the comments) to get a very fast solution:我们可以结合这些想法(以及评论中 Mark Dickinson 和 Eric Postpischil 的改进)来获得一个非常快速的解决方案:

from math import gcd
from functools import reduce

def lcm(a,b):
    return a // gcd(a,b) * b

def get_smallest_number2(n):
    return reduce(lcm,range(1 + n//2,n+1),1)

Some quick %timeit results in IPython: IPython 中的一些快速%timeit结果:

%timeit get_smallest_number2(15)
2.07 µs ± 26.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit get_smallest_number(15)
443 ms ± 5.75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

For n = 15 it is thus over 200,000 times faster.对于n = 15 ,它因此快 200,000 倍以上。 Your function fails to produce any output long before n = 100 , but get_smallest_number2(100) evaluates to 69720375229712477164533808935312303556800 almost instantly.您的 function 早在n = 100之前就无法产生任何 output ,但get_smallest_number2(100)几乎立即计算为69720375229712477164533808935312303556800

Start by thinking about the factorial n., This is obviously divisible by all numbers less then n.首先考虑阶乘 n。这显然可以被所有小于 n 的数整除。 but is not the smallest such number, For example, you could divide n, by 6. and the smaller result would still be divisible by 2 and by 3, and hence still divisible by 6.但不是这样的最小数字,例如,您可以将 n 除以 6。较小的结果仍可被 2 和 3 整除,因此仍可被 6 整除。

What numbers can we divide out?我们可以划分出哪些数字? Composites, like 6, don't matter as long as all the required primes are present: 2 and 3 in that case.只要存在所有必需的素数,复合材料(如 6)就无关紧要:在这种情况下为 2 和 3。 The primes give you the composites for free.素数免费为您提供复合材料。 So, concentrate on the primes.所以,专注于素数。

Start with 2. Look at the powers of 2: 2, 4, 8, 16, ... Run through the powers of 2 until you find the highest power that is smaller than or equal to n.从 2 开始。查看 2 的幂:2、4、8、16、... 遍历 2 的幂,直到找到小于或等于 n 的最高幂。 That is the only power of 2 you need, all the lower powers are unnecessary.这是您需要的唯一 2 的幂,所有较低的幂都是不必要的。 You don't need to include 4 explicitly if n is 8 or higher because then you will have 8, 16 or whatever as a multiplier.如果 n 为 8 或更高,则不需要明确包含 4,因为这样您将拥有 8、16 或其他任何乘数。 Repeat for powers of 3: 3, 9, 27, 81, ... and so on through the primes up to sqrt(n).重复 3 的幂:3, 9, 27, 81, ... 直到 sqrt(n) 的素数。 Beyond that point you only need the remaining primes less than n, since higher powers of those primes will exceed n.超过这一点,您只需要小于 n 的剩余素数,因为这些素数的更高幂将超过 n。

Multiply the selected prime powers together to get the least n.将选定的素数乘以得到最小的 n。

Use a Sieve of Eratosthenes to generate your initial list of primes up to n.使用埃拉托色尼筛法生成初始素数列表,最多为 n。

Another way to implement LCM实现 LCM 的另一种方法

import time
from datetime import timedelta


start_time = time.monotonic()
 

def lcm(nums):
    res = 1
    for i in nums:
        res = (res * i) // gcd(res, i)
    return res
    
def gcd(a, b):
    while b:
        a, b = b, a%b
    return a
    
print(lcm([8, 9, 10, 11, 12, 13, 14, 15]))




end_time = time.monotonic()
print(f'Duration: {timedelta(seconds=end_time - start_time)}')

Produces生产

360360
Duration: 0:00:00.000061

[Program finished]

The idea is to add highest divider every iteration and check from high to low.这个想法是在每次迭代中添加最高分频器并从高到低检查。 Something like this:像这样的东西:

n = int(input("n = "))
result = 0
while True:
    result += n
    for i in range(n, 1, -1):
        if result % i != 0:
            break
    else:
        break
print(result)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM